Abstract

Obesity is a growing global health challenge, and this study aims to predict obesity levels based solely on lifestyle and eating habits. This study develops and compares four tree-based machine learning models to predict obesity classification from demographic and behavioral data. Models evaluated include single decision trees using deviance and Gini splitting criteria, bagging, and random forest. The dataset contains 531 observations with 15 predictor variables including age, height, water intake, eating frequency, physical activity, and family history. Random forest emerged as the best model with 31.8% total error and AUC of 0.75. Age and height are the strongest predictors of obesity, followed by behavioral factors like water intake and eating frequency. Ensemble methods significantly outperform single trees, with random forest achieving 31.8% error compared to 33.6% for single decision trees. The main challenge is classifying overweight cases due to class imbalance and overlap with other obesity categories. Results show that demographic and lifestyle factors can predict obesity reasonably well, but prediction accuracy varies across obesity classes.


1 Introduction

1.1 Background and Motivation

Obesity is a major public health challenge. The World Health Organization (WHO) reports that global obesity prevalence has nearly tripled since 1975. Current data indicates that over one billion people live with obesity, including approximately 890 million adults and 160 million children and adolescents. Incidence rates are rising in low- and middle-income countries, where healthcare systems often face a dual burden of malnutrition and metabolic disease. This condition drives non-communicable diseases, including type 2 diabetes, cardiovascular disease, and cancer, through mechanisms such as systemic inflammation and insulin resistance. The economic impact of obesity is substantial. The World Obesity Federation projects that the global cost of overweight and obesity will reach $4.32 trillion annually by 2035, equivalent to nearly 3% of global GDP. Indirect costs, stemming from lost workforce productivity, absenteeism, and premature mortality, often exceed direct medical expenditures. Consequently, the failure to address obesity affects national economies alongside healthcare systems. Addressing this challenge requires moving from reactive management to early identification. Current screening relies on Body Mass Index (BMI), a lagging indicator that may not capture body composition nuances or identify risk before weight gain occurs. Predictive tools that identify at-risk individuals based on modifiable behaviors, such as diet and physical activity, are necessary. This study addresses this gap by developing and benchmarking machine learning classification models to predict obesity status using demographic and lifestyle characteristics.


1.2 Dataset Description

1.2.1 Data Source

The dataset originates from the study by Palechor and de la Hoz Manotas (2019), titled “Dataset for estimation of obesity levels based on eating habits and physical condition in individuals from Colombia, Peru and Mexico.” Data was collected via an online questionnaire administered to individuals aged 14-61 years residing in Colombia, Peru, and Mexico. The original dataset contains 2,111 observations. However, approximately 75% of these observations were synthetically generated using SMOTE (Synthetic Minority Over-sampling Technique) to balance the obesity classes. This synthetic augmentation is a critical consideration addressed in our data preparation.

1.2.2 Variable Descriptions

The following table describes all variables in the dataset:

Variable Type Description Levels/Units
Demographic Variables
Gender Categorical Biological sex Female, Male
Age Numeric Age in years 14-61 years
Height Numeric Height Meters
Weight Numeric Weight Kilograms
Eating Habits
FAVC Binary Frequent consumption of high-calorie food No, Yes
FCVC Ordinal Frequency of vegetable consumption 1=Never, 2=Sometimes, 3=Always
NCP Ordinal Number of main meals per day 1, 2, 3, 4+
CAEC Categorical Consumption of food between meals No, Sometimes, Frequently, Always
CH2O Ordinal Daily water consumption 1=Low, 2=Medium, 3=High
CALC Categorical Alcohol consumption frequency No, Sometimes, Frequently, Always
Lifestyle Variables
SMOKE Binary Smoking status No, Yes
SCC Binary Calorie consumption monitoring No, Yes
FAF Ordinal Physical activity frequency 0=None, 1=1-2 days, 2=2-4 days, 3=4+ days
TUE Ordinal Time using technology devices 0=0-3h, 1=3-5h, 2=5h+
MTRANS Categorical Transportation used Automobile, Bike, Motorbike, Public Transport, Walking
family_history_with_overweight Binary Family history of overweight No, Yes
Target Variable
NObeyesdad Categorical Obesity level classification 7 levels (Insufficient Weight to Obesity Type III)

1.3 Research Questions

This analysis addresses the following research question:

What demographic and behavioral factors best predict obesity risk, and which machine learning method provides the most accurate classification?

2 Data Preparation

library(tree)
library(randomForest)
library(pROC)
library(ggplot2)
library(knitr)
library(kableExtra)
set.seed(123)  

2.1 Real vs. Synthetic Data Separation

2.1.1 Rationale

The original dataset contains both real survey responses and synthetically generated observations (via SMOTE). Using synthetic data may introduce artifacts that do not reflect human behavior patterns. To ensure our findings are valid and generalizable, we identify and filter out synthetic observations.

Identification Method: Real survey responses have integer values for variables FCVC, NCP, CH2O, FAF, and TUE, while SMOTE-generated records contain decimal values due to the interpolation process.


obesity <- read.csv("ObesityDataSet_raw_and_data_sinthetic.csv")
is_decimal <- function(x) { return((x %% 1) != 0)}
obesity$data_type <- "Real"
synthetic_indices <- which( is_decimal(obesity$FCVC) |is_decimal(obesity$NCP) | is_decimal(obesity$CH2O) | is_decimal(obesity$FAF) | is_decimal(obesity$TUE))
obesity$data_type[synthetic_indices] <- "Synthetic"
table(obesity$data_type)

     Real Synthetic 
      531      1580 
prop.table(table(obesity$data_type)) * 100

     Real Synthetic 
 25.15396  74.84604 
obesity_real <- obesity[obesity$data_type == "Real", ]

2.1.2 Interpretation

We proceed with only the 531 real observations to ensure validity of findings.


2.2 Variable Transformations

2.2.1 Factor Conversions

All categorical variables must be properly converted to R factors for modeling. This ensures correct treatment in tree-based methods.

# Convert binary variables: FAVC, SMOKE, SCC, family_history, Gender
obesity_real$FAVC  <- factor(obesity_real$FAVC,  levels = c("no", "yes"))
obesity_real$SMOKE <- factor(obesity_real$SMOKE, levels = c("no", "yes"))
obesity_real$SCC   <- factor(obesity_real$SCC,   levels = c("no", "yes"))
obesity_real$family_history_with_overweight <- factor(obesity_real$family_history_with_overweight,levels = c("no", "yes"))
obesity_real$Gender <- factor(obesity_real$Gender, levels = c("Female", "Male"))
# Convert ordinal variables: FCVC, CH2O, FAF, TUE
obesity_real$FCVC <- factor(obesity_real$FCVC, levels = c(1, 2, 3), ordered = TRUE)
obesity_real$CH2O <- factor(obesity_real$CH2O, levels = c(1, 2, 3), ordered = TRUE)
obesity_real$FAF <- factor(obesity_real$FAF, levels = c(0, 1, 2, 3), ordered = TRUE)
obesity$NCP  <- factor(obesity$NCP,  levels = c(1, 2, 3, 4))
obesity_real$TUE <- factor(obesity_real$TUE, levels = c(0, 1, 2), ordered = TRUE)
# Convert nominal variables: CAEC, MTRANS, NObeyesdad
obesity_real$CAEC <- factor(obesity_real$CAEC, levels = c("no", "Sometimes", "Frequently", "Always"))
obesity$CALC <- factor(obesity$CALC,levels = c("no", "Sometimes", "Frequently", "Always"))
obesity$MTRANS <- factor(obesity$MTRANS, levels = c("Automobile", "Bike", "Motorbike", "Public_Transportation", "Walking"))
obesity$NObeyesdad <- factor(obesity$NObeyesdad,levels=c("Insufficient_Weight","Normal_Weight","Overweight_Level_I","Overweight_Level_II","Obesity_Type_I","Obesity_Type_II","Obesity_Type_III"))
str(obesity_real)
'data.frame':   531 obs. of  18 variables:
 $ Gender                        : Factor w/ 2 levels "Female","Male": 1 1 2 2 2 2 1 2 2 2 ...
 $ Age                           : num  21 21 23 27 22 29 23 22 24 22 ...
 $ Height                        : num  1.62 1.52 1.8 1.8 1.78 1.62 1.5 1.64 1.78 1.72 ...
 $ Weight                        : num  64 56 77 87 89.8 53 55 53 64 68 ...
 $ family_history_with_overweight: Factor w/ 2 levels "no","yes": 2 2 2 1 1 1 2 1 2 2 ...
 $ FAVC                          : Factor w/ 2 levels "no","yes": 1 1 1 1 1 2 2 1 2 2 ...
 $ FCVC                          : Ord.factor w/ 3 levels "1"<"2"<"3": 2 3 2 3 2 2 3 2 3 2 ...
 $ NCP                           : num  3 3 3 3 1 3 3 3 3 3 ...
 $ CAEC                          : Factor w/ 4 levels "no","Sometimes",..: 2 2 2 2 2 2 2 2 2 2 ...
 $ SMOKE                         : Factor w/ 2 levels "no","yes": 1 2 1 1 1 1 1 1 1 1 ...
 $ CH2O                          : Ord.factor w/ 3 levels "1"<"2"<"3": 2 3 2 2 2 2 2 2 2 2 ...
 $ SCC                           : Factor w/ 2 levels "no","yes": 1 2 1 1 1 1 1 1 1 1 ...
 $ FAF                           : Ord.factor w/ 4 levels "0"<"1"<"2"<"3": 1 4 3 3 1 1 2 4 2 2 ...
 $ TUE                           : Ord.factor w/ 3 levels "0"<"1"<"2": 2 1 2 1 1 1 1 1 2 2 ...
 $ CALC                          : chr  "no" "Sometimes" "Frequently" "Frequently" ...
 $ MTRANS                        : chr  "Public_Transportation" "Public_Transportation" "Public_Transportation" "Walking" ...
 $ NObeyesdad                    : chr  "Normal_Weight" "Normal_Weight" "Normal_Weight" "Overweight_Level_I" ...
 $ data_type                     : chr  "Real" "Real" "Real" "Real" ...

2.2.2 Merging Rare Factor Levels

Several categorical variables contain levels with very few observations, which can cause modeling issues. We merge these into meaningful grouped categories.

2.2.2.1 Transportation Mode (MTRANS)

cat("Original MTRANS Counts:\n")
Original MTRANS Counts:
print(table(obesity_real$MTRANS))

           Automobile                  Bike             Motorbike Public_Transportation               Walking 
                  107                     7                    11                   351                    55 
obesity_real$MTRANS_3 <- as.character(obesity_real$MTRANS)
obesity_real$MTRANS_3[obesity_real$MTRANS %in% c("Bike", "Walking")] <- "Active"
obesity_real$MTRANS_3[obesity_real$MTRANS %in% c("Automobile", "Motorbike")] <- "Private_Motor"
obesity_real$MTRANS_3[obesity_real$MTRANS == "Public_Transportation"] <- "Public_Transport"
obesity_real$MTRANS_3 <- factor(obesity_real$MTRANS_3, levels = c("Active", "Private_Motor", "Public_Transport"))
cat("\nMerged MTRANS_3 Counts:\n")

Merged MTRANS_3 Counts:
print(table(obesity_real$MTRANS_3))

          Active    Private_Motor Public_Transport 
              62              118              351 

Rationale: The categories “Bike” and “Motorbike” had extremely low frequencies, leading to sparsity. To address this, we grouped categories based on physical exertion: “Active” (Walking and Bike) and “Private Motor” (Automobile and Motorbike).

2.2.2.2 Number of Meals (NCP)

cat("Original NCP Counts:\n")
Original NCP Counts:
print(table(obesity_real$NCP))

  1   3   4 
124 359  48 
obesity_real$NCP_3 <- NA_character_
ncp_numeric <- as.numeric(as.character(obesity_real$NCP)) # Ensure we treat it as number for logic
obesity_real$NCP_3[ncp_numeric <= 2] <- "Low_1-2"
obesity_real$NCP_3[ncp_numeric == 3] <- "Normal_3"
obesity_real$NCP_3[ncp_numeric >= 4] <- "High_4+"
obesity_real$NCP_3 <- factor(obesity_real$NCP_3, levels = c("Low_1-2", "Normal_3", "High_4+"))
cat("\nMerged NCP_3 Counts:\n")

Merged NCP_3 Counts:
print(table(obesity_real$NCP_3))

 Low_1-2 Normal_3  High_4+ 
     124      359       48 

Rationale: Most people consume 3 meals a day (Normal). Those eating 1 or 2 meals likely have caloric deficits or skipping habits, while 4+ suggests frequent eating.

2.2.2.3 Alcohol Consumption (CALC)

print(table(obesity_real$CALC))

    Always Frequently         no  Sometimes 
         1         45        191        294 
obesity_real$CALC_3 <- as.character(obesity_real$CALC)
obesity_real$CALC_3[obesity_real$CALC == "no"] <- "No"
obesity_real$CALC_3[obesity_real$CALC == "Sometimes"] <- "Sometimes"
obesity_real$CALC_3[obesity_real$CALC %in% c("Frequently", "Always")] <- "Frequent"
obesity_real$CALC_3 <- factor(obesity_real$CALC_3, levels = c("No", "Sometimes", "Frequent"))
print(table(obesity_real$CALC_3))

       No Sometimes  Frequent 
      191       294        46 

Rationale: The “Always” category contained negligible observations in the real dataset. We merged “Always” with “Frequently” into a single “Frequent” category.

2.2.3 Binning Continuous Variables

Continuous variables (Age, Height) are binned into quantile-based categories to avoid assumptions of linear relationships.

make_quantile_bins <- function(x, n_bins = 5, labels = NULL) {
  probs <- seq(0, 1, length.out = n_bins + 1)
  qs <- unique(quantile(x, probs = probs, na.rm = TRUE))
  # Create bins
  cut(x, breaks = qs, include.lowest = TRUE, labels = labels[1:(length(qs)-1)])}
obesity_real$Age_bin5 <- make_quantile_bins( obesity_real$Age,n_bins = 5, labels = c("Very_Young", "Young", "Adult", "Mature", "Older"))
obesity_real$Height_bin5 <- make_quantile_bins( obesity_real$Height, n_bins = 5,labels = c("Very_Short", "Short", "Medium", "Tall", "Very_Tall"))
print(table(obesity_real$Age_bin5))

Very_Young      Young      Adult     Mature      Older 
       110        176         37        113         95 
barplot(table(obesity_real$Age_bin5), main="Age Groups", col="gray")

print(table(obesity_real$Height_bin5))

Very_Short      Short     Medium       Tall  Very_Tall 
       125        105         96         99        106 
barplot(table(obesity_real$Height_bin5), main="Height Groups", col="gray")

Note on Weight: Weight is intentionally excluded from the predictor set because obesity classification is derived from BMI (which uses weight). Including weight create circular logic and inflate model performance.

2.2.4 Target Variable Creation

The original 7-class target variable is grouped into 3 classes to improve model stability given our reduced sample size.

obesity_real$Obesity_3 <- NA_character_
obesity_real$Obesity_3[obesity_real$NObeyesdad %in% c("Insufficient_Weight", "Normal_Weight")] <- "Normal_or_Under"
obesity_real$Obesity_3[obesity_real$NObeyesdad %in% c("Overweight_Level_I", "Overweight_Level_II")] <- "Overweight"
obesity_real$Obesity_3[obesity_real$NObeyesdad %in% c("Obesity_Type_I", "Obesity_Type_II", "Obesity_Type_III")] <- "Obese"
obesity_real$Obesity_3 <- factor(obesity_real$Obesity_3, levels = c("Normal_or_Under", "Overweight", "Obese"))
cat("--- Target Variable Distribution (3 Classes) ---\n")
--- Target Variable Distribution (3 Classes) ---
print(table(obesity_real$Obesity_3))

Normal_or_Under      Overweight           Obese 
            327             132              72 
print(prop.table(table(obesity_real$Obesity_3)) * 100) # Show percentages

Normal_or_Under      Overweight           Obese 
       61.58192        24.85876        13.55932 
barplot(table(obesity_real$Obesity_3), 
        main = "Target Distribution (3 Classes)", 
        col = c("green", "orange", "red"),
        ylab = "Count")

Rationale: With only 531 real observations, the 7-class problem would have insufficient samples per class for reliable modeling. The 3-class grouping ensure adequate class sizes.


2.3 Train/Test Split

set.seed(123)
n <- nrow(obesity_real)
train_size <- floor(0.8 * n)
train_index <- sample(1:n, size = train_size, replace = FALSE)
ob_train <- obesity_real[train_index, ]
ob_test  <- obesity_real[-train_index, ]

2.3.1 Class Distribution Verification

Class Training Set (n=424) Test Set (n=107)
Normal_or_Under 62% 59.8%
Overweight 25% 24.3%
Obese 13% 15.9%

The similar class distributions confirm that our random split produced representative training and test sets.


2.4 Final Predictor Set

Based on the transformations above, our final predictor set consists of 15 variables:

allowed_predictors <- c("Gender","Age_bin5","Height_bin5","family_history_with_overweight","FAVC",
  "FCVC","CAEC","SMOKE","CH2O","SCC","FAF","TUE","NCP_3","CALC_3","MTRANS_3")

Excluded variables:

  • Weight: Circular relationship with target (BMI uses weight)
  • Height (raw): Replaced with Height_bin5
  • Age (raw): Replaced with Age_bin5
  • Original MTRANS, NCP, CALC: Replaced with merged versions

3 Exploratory Data Analysis

3.1 Target Variable Distribution

target_counts <- table(obesity_real$Obesity_3)
target_pct <- round(prop.table(target_counts) * 100, 1)
bp <- barplot(target_counts, main = "Distribution of Obesity Levels (3 Classes)", col = c("forestgreen", "orange", "firebrick"), ylim = c(0, max(target_counts) * 1.2),ylab = "Count")
text(x = bp, y = target_counts, labels = paste0(target_counts, "\n(", target_pct, "%)"), pos = 3, cex = 0.9)

3.1.1 Interpretation

The distribution of the target variable is clearly imbalanced. This class imbalance implies that Models may naturally favor the majority class. methods robust to uneven class sizes such as Random Forest may work better when predicting obesity levels.


3.2 Predictor Distributions

3.2.1 Demographic Variables

par(mfrow = c(1, 3))
gender_counts <- table(obesity_real$Gender)
bp_g <- barplot(gender_counts, main = "Gender Distribution", col = c("lightpink", "lightblue"),ylim = c(0, max(gender_counts) * 1.15))
text(bp_g, gender_counts, labels = gender_counts, pos = 3)
age_counts <- table(obesity_real$Age_bin5)
bp_a <- barplot(age_counts, main = "Age Groups (Quantiles)", col = "gray80",las = 2,cex.names = 0.8)
height_counts <- table(obesity_real$Height_bin5)
bp_h <- barplot(height_counts, main = "Height Groups (Quantiles)", col = "gray80",las = 2,cex.names = 0.8)
par(mfrow = c(1, 1))

Key observations:

  • There is no severe gender imbalance that would bias the model toward one group. Height and Age is unlikely to introduce bias. It may play a role indirectly, as height interacts with BMI-related physiology.

3.2.2 Eating Habit Variables

par(mfrow = c(2, 3))
barplot(table(obesity_real$FAVC),main = "High Calorie Food (FAVC)", col = "lightblue", las = 1)
barplot(table(obesity_real$FCVC), main = "Vegetable Cons. (FCVC)", xlab = "Level (1=Never, 3=Always)",col = "lightblue", las = 1)
barplot(table(obesity_real$NCP_3),main = "Number of Meals (NCP)",col = "lightblue", las = 1)
barplot(table(obesity_real$CAEC), main = "Snacking Habits (CAEC)",col = "lightblue", las = 1, cex.names = 0.8)
barplot(table(obesity_real$CH2O), main = "Daily Water Intake (CH2O)",xlab = "Level (1=Low, 3=High)",col = "lightblue", las = 1)
barplot(table(obesity_real$CALC_3), main = "Alcohol Cons. (CALC)", col = "lightblue", las = 1)
par(mfrow = c(1, 1))

Key observations:

  • Approximately 90% of participants answered “Yes” to FAVC. Since almost everyone consumes high-calorie food, this variable has low variance and may not effectively distinguish between Obese and Normal individuals in the decision tree. in other hand the FCVC data shows a good spread across all three levels. The balanced distribution suggests this will be a strong predictor for distinguishing health-conscious individuals.

3.2.3 Lifestyle Variables

par(mfrow = c(2, 3))

barplot(table(obesity_real$SMOKE),main = "Smoking Status", col = "lightsalmon", las = 1)
barplot(table(obesity_real$SCC),main = "Calorie Monitoring (SCC)", col = "lightsalmon", las = 1)
barplot(table(obesity_real$FAF),main = "Physical Activity (FAF)", xlab = "Level (0=None, 3=High)",col = "lightsalmon", las = 1)
barplot(table(obesity_real$TUE), main = "Tech Use Time (TUE)", xlab = "Level (0=Low, 2=High)",col = "lightsalmon", las = 1)
barplot(table(obesity_real$MTRANS_3), main = "Transport Mode (MTRANS)", col = "lightgreen", las = 2, cex.names = 0.8)
barplot(table(obesity_real$family_history_with_overweight), main = "Family History (Overweight)", col = "lightsalmon", las = 1)
par(mfrow = c(1, 1))

Key observations:

The most influential among the lifestyle variables is expected to be the family history of overweight. This variable shows a sharp and meaningful contrast between groups and captures a combined effect of genetic predisposition and long-term shared environment.Our model results indeed confirm that this variable becomes one of the strongest predictors and often appears among the first splits in decision trees and top-ranked features in Random Forest importance measures.


3.3 Relationships with Target

3.3.1 Key Predictor-Target Associations


plot_bivariate_base <- function(data, x_var, fill_var, title, col_palette = NULL) {tab <- table(data[[x_var]], data[[fill_var]])
prop_tab <- prop.table(tab, margin = 1)
  if (is.null(col_palette)) {col_palette <- rainbow(ncol(prop_tab))}
  barplot(t(prop_tab), beside = FALSE,col = col_palette,main = title,xlab = "Obesity Class", ylab = "Percentage within Class",ylim = c(0, 1),las = 1) 
  legend("topright", legend = rev(colnames(prop_tab)),  fill = rev(col_palette),title = fill_var,cex = 0.6,bty = "o",bg = "white")   }
par(mfrow = c(2, 2), mar = c(5, 4, 4, 2))   
col_pastel1 <- c("#FBB4AE", "#B3CDE3")  
plot_bivariate_base(obesity_real, "Obesity_3", "family_history_with_overweight", "Family History by Obesity Class", col_pastel1)
col_blues <- c("#EFF3FF", "#BDD7E7", "#6BAED6", "#2171B5")
plot_bivariate_base(obesity_real, "Obesity_3", "FAF", "Physical Activity (FAF) by Obesity Class", col_blues)
col_greys <- c("#F7F7F7", "#CCCCCC", "#969696", "#525252")
plot_bivariate_base(obesity_real, "Obesity_3", "Age_bin5", "Age Distribution by Obesity Class", col_greys)
col_oranges <- c("#FEE6CE", "#E6550D")
plot_bivariate_base(obesity_real, "Obesity_3", "FAVC", "High Calorie Food Consumption by Obesity Class", col_oranges)
par(mfrow = c(1, 1))

3.3.2 Interpretation

The plots show that high-calorie food consumption is ubiquitous, with nearly 90% of the entire population (regardless of weight class) reporting “Yes.” While the Obese group does show a slightly higher saturation of “Yes” responses compared to the Normal group, the lack of a sharp contrast suggests that what people eat (high calorie) is less predictive than how much they eat (NCP) or their genetic background (Family History)


4 Methodology

4.1 Decision Trees

Decision trees partition the predictor space into regions through recursive binary splitting. At each node, the algorithm selects the variable and split point that best separates the classes according to an impurity measure.

Splitting Criteria:

  • Deviance (Entropy): Measures information gain; prefers splits that create purer nodes
  • Gini Index: Measures class probability dispersion; commonly used in CART

Cross-Validation for Tree Size: Large trees tend to overfit the training data. We use K-fold cross-validation to identify the optimal tree size that minimizes prediction error on held-out data.

Pruning: After determining the optimal size via cross-validation, we prune the tree by removing splits that do not improve cross-validated performance. This produces a simpler, more generalizable model.

R Implementation: We use the tree package with cv.tree() for cross-validation and prune.misclass() for pruning based on misclassification rate.


4.2 Bagging (Bootstrap Aggregating)

Bagging reduces variance by training multiple trees on bootstrap samples of the training data and averaging their predictions.

Key characteristics:

  • Each tree is trained on a bootstrap sample (sampling with replacement)
  • At each split, all p predictors are considered (mtry = p)
  • Final prediction is by majority vote across all trees
  • Out-of-bag (OOB) observations provide built-in validation

R Implementation: We use randomForest() with mtry = p (number of predictors = 15) and ntree = 500.


4.3 Random Forest

Random Forest extends bagging by introducing additional randomness: at each split, only a random subset of predictors is considered.

Key characteristics:

  • Same bootstrap sampling as bagging
  • At each split, only √p predictors are randomly selected (mtry ≈ 4 for our 15 predictors)
  • This decorrelates the trees, further reducing variance
  • Variable importance measures indicate predictor contributions

R Implementation: We use randomForest() with default mtry (√p for classification) and ntree = 500.


5 Results

5.1 Decision Tree Results

5.1.1 Model 1: Deviance-Based Tree

train_data <- ob_train[, c("Obesity_3", allowed_predictors)]
test_data  <- ob_test[, c("Obesity_3", allowed_predictors)]
tree_deviance <- tree(Obesity_3 ~ ., data = train_data, split = "deviance")
summary(tree_deviance)

Classification tree:
tree(formula = Obesity_3 ~ ., data = train_data, split = "deviance")
Variables actually used in tree construction:
 [1] "Age_bin5"                       "CAEC"                           "Height_bin5"                   
 [4] "FAVC"                           "CH2O"                           "CALC_3"                        
 [7] "family_history_with_overweight" "TUE"                            "NCP_3"                         
[10] "FAF"                           
Number of terminal nodes:  18 
Residual mean deviance:  1.319 = 535.5 / 406 
Misclassification error rate: 0.2642 = 112 / 424 
plot(tree_deviance); text(tree_deviance, pretty=0)

Our Deviance tree model predicts obesity classification using 18 different groups based on 10 variables. The model correctly classifies about 73.6% of cases, which means it makes mistakes about 26.4% of the time. Age is the first split, and then the model uses eating frequency, height, and water consumption to make further decisions. The error rate shows that obesity is hard to predict with just these features because different obesity groups have similar characteristics.

5.1.1.1 Cross-Validation for Optimal Size

set.seed(2024)
cv_deviance <- cv.tree(tree_deviance, FUN = prune.misclass)
print(cv_deviance)
$size
[1] 18 16 10  6  5  1

$dev
[1] 158 157 151 154 156 162

$k
[1] -Inf 0.00 1.00 3.50 4.00 6.25

$method
[1] "misclass"

attr(,"class")
[1] "prune"         "tree.sequence"
plot(cv_deviance$size, cv_deviance$dev, type = "b", pch = 19, col = "darkred", lwd = 2,xlab = "Tree Size (Number of Terminal Nodes)", ylab = "CV Misclassification Error",main = "Cross-Validation: Error vs Complexity")
grid()

best_size_dev <- cv_deviance$size[which.min(cv_deviance$dev)]
cat("\nOptimal Tree Size (Nodes):", best_size_dev, "\n")

Optimal Tree Size (Nodes): 10 

Cross-validation was used to find the best tree size that balances accuracy and simplicity. The results show that a tree with 10 nodes has the lowest cross-validation error of 151 misclassifications. This is better than the original tree with 18 nodes, which had 158 errors. A smaller tree is easier to understand and works better on new data because it avoids overfitting. The optimal tree size of 10 nodes removes unnecessary splits while keeping good prediction performance.

5.1.1.2 Pruned Tree Visualization

tree_deviance_pruned <- prune.misclass(tree_deviance, best = best_size_dev)
plot(tree_deviance_pruned)
text(tree_deviance_pruned, pretty = 0, cex = 0.8, col = "blue")
title(paste("Pruned Deviance Tree (Size =", best_size_dev, ")"))

The pruned tree with 10 nodes is simpler and easier to interpret than the original tree. The main splits are still age, eating frequency (CAEC), and height, which are the most important factors for obesity. For younger people who do not eat outside frequently, height determines whether they are normal weight or overweight. For older people, the tree checks family history, technology use time (TUE), number of meals (NCP_3), and physical activity (FAF) to make the final prediction. The pruned tree removes complex branches that do not help much with prediction, making it a cleaner model that is better for understanding obesity patterns.

5.1.1.3 Test Set Performance

pred_class_dev <- predict(tree_deviance_pruned, newdata = test_data, type = "class")
pred_prob_dev <- predict(tree_deviance_pruned, newdata = test_data, type = "vector")
conf_matrix_dev <- table(Predicted = pred_class_dev, Actual = test_data$Obesity_3)
print(conf_matrix_dev)
                 Actual
Predicted         Normal_or_Under Overweight Obese
  Normal_or_Under              62         18    10
  Overweight                    2          6     4
  Obese                         0          2     3
total_error_dev <- mean(pred_class_dev != test_data$Obesity_3)
class_errors_dev <- 1 - diag(conf_matrix_dev) / colSums(conf_matrix_dev)
roc_dev <- multiclass.roc(test_data$Obesity_3, pred_prob_dev)
auc_dev <- auc(roc_dev)

Performance Metrics:

Metric Value
Total Error Rate 33.64%
Normal_or_Under Error 3.12%
Overweight Error 76.92%
Obese Error 82.35%
Multiclass AUC .6489

On the test set, the model has a total error rate of 33.64%, meaning it correctly predicts about 66% of cases. The model performs well at identifying normal weight people, with only 3.12% error. However, it struggles with overweight and obese groups, with error rates of 76.92% and 82.35%. This means the model often mistakes overweight and obese people as normal weight. The multiclass AUC score of 0.65 shows moderate performance. The confusion matrix reveals that most prediction errors come from confusing overweight and obese cases with normal weight, suggesting the model needs better ability to distinguish between these heavier weight groups.


5.1.2 Model 2: Gini-Based Tree

tree_gini <- tree(Obesity_3 ~ ., data = train_data, split = "gini")
summary(tree_gini)

Classification tree:
tree(formula = Obesity_3 ~ ., data = train_data, split = "gini")
Variables actually used in tree construction:
 [1] "Age_bin5"                       "CH2O"                           "family_history_with_overweight"
 [4] "SCC"                            "Gender"                         "Height_bin5"                   
 [7] "MTRANS_3"                       "FCVC"                           "CALC_3"                        
[10] "FAF"                            "TUE"                            "CAEC"                          
[13] "NCP_3"                          "FAVC"                          
Number of terminal nodes:  53 
Residual mean deviance:  0.9789 = 363.2 / 371 
Misclassification error rate: 0.2052 = 87 / 424 
set.seed(2024)
cv_gini <- cv.tree(tree_gini, FUN = prune.misclass)
plot(cv_gini$size, cv_gini$dev, type = "b", pch = 19, col = "darkblue", lwd = 2,xlab = "Tree Size", ylab = "CV Misclassification Error",main = "CV Error (Gini Split)")
grid()

best_size_gini <- cv_gini$size[which.min(cv_gini$dev)]
cat("\nOptimal Tree Size (Gini):", best_size_gini, "\n")

Optimal Tree Size (Gini): 23 
tree_gini_pruned <- prune.misclass(tree_gini, best = best_size_gini)
plot(tree_gini_pruned)
text(tree_gini_pruned, pretty = 0, cex = 0.8, col = "darkgreen")
title(paste("Pruned Gini Tree (Size =", best_size_gini, ")"))

pred_class_gini <- predict(tree_gini_pruned, newdata = test_data, type = "class")
pred_prob_gini <- predict(tree_gini_pruned, newdata = test_data, type = "vector")
conf_matrix_gini <- table(Predicted = pred_class_gini, Actual = test_data$Obesity_3)
print(conf_matrix_gini)
                 Actual
Predicted         Normal_or_Under Overweight Obese
  Normal_or_Under              55         16     5
  Overweight                    7          8     4
  Obese                         2          2     8
total_error_gini <- mean(pred_class_gini != test_data$Obesity_3)
class_errors_gini <- 1 - diag(conf_matrix_gini) / colSums(conf_matrix_gini)
roc_gini <- multiclass.roc(test_data$Obesity_3, pred_prob_gini)
auc_gini <- auc(roc_gini)

Performance Metrics:

Metric Value
Total Error Rate 33.64%
Normal_or_Under Error 14.06%
Overweight Error 69.23%
Obese Error 52.94%
Multiclass AUC 0.7377

A second tree model was built using Gini instead of deviance as the splitting criterion. The Gini tree created 53 nodes before pruning, with a training error rate of 20.52%, which is better than the deviance tree. Cross-validation showed that the optimal tree size is 23 nodes. This larger tree uses more variables including water intake, family history, gender, transportation method (MTRANS_3), and vegetable consumption (FCVC), showing that Gini considers different factors important. On the test set, the Gini tree has the same total error rate of 33.64% as the deviance tree, but the error distribution is different. It has higher error for normal weight people (14.06%) but lower errors for overweight (69.23%) and obese (52.94%) groups. The multiclass AUC of 0.74 is better than the deviance tree’s 0.65, indicating the Gini tree is more effective at distinguishing between obesity classes.


5.2 Bagging Results

set.seed(208)
p <- length(allowed_predictors)
bag_model <- randomForest(Obesity_3 ~ ., data = train_data,mtry = p,ntree = 500, importance =TRUE)
cat("--- Bagging Model Summary ---\n")
--- Bagging Model Summary ---
print(bag_model)

Call:
 randomForest(formula = Obesity_3 ~ ., data = train_data, mtry = p,      ntree = 500, importance = TRUE) 
               Type of random forest: classification
                     Number of trees: 500
No. of variables tried at each split: 15

        OOB estimate of  error rate: 31.6%
Confusion matrix:
                Normal_or_Under Overweight Obese class.error
Normal_or_Under             238         19     6  0.09505703
Overweight                   51         44    11  0.58490566
Obese                        36         11     8  0.85454545

Bagging was used to improve predictions by combining 500 decision trees. In bagging, each tree is built from a random sample of the training data, and predictions are made by averaging results across all trees. The bagging model uses all 15 predictor variables at each split. The out-of-bag (OOB) error rate is 31.6%, which is lower than the single tree models. The error rates for each class show that bagging performs well for normal weight people (9.5% error) but still struggles with overweight (58.5% error) and obese (85.5% error) groups. Bagging reduces overfitting compared to single trees, but the class imbalance problem remains—the model is good at identifying normal weight people but makes many mistakes on heavier groups.

5.2.1 Variable Importance

par(mfrow = c(1, 2))
varImpPlot(bag_model, main = "Bagging: Variable Importance")
par(mfrow = c(1, 1))

cat("\n--- Variable Importance ---\n")

--- Variable Importance ---
importance(bag_model)
                               Normal_or_Under Overweight      Obese MeanDecreaseAccuracy MeanDecreaseGini
Gender                               11.018457  1.9209436  4.5121991            11.656263         6.841538
Age_bin5                             20.788830 16.8762955 10.9801202            28.551234        30.976471
Height_bin5                          13.058442 14.6259492  4.5438254            19.821359        30.445264
family_history_with_overweight        8.038287  4.8179263 13.0884633            12.172318         9.617167
FAVC                                  8.708945 -0.2074888  4.2528992             8.402860         7.738184
FCVC                                  3.635740 10.1500667  2.5528119             9.269282        16.206663
CAEC                                  4.317601 17.4089668  6.1233649            15.335813        19.619300
SMOKE                                 6.137518 -2.4774282  2.5347723             4.585786         5.655272
CH2O                                  5.473174 12.0804602 14.3994823            16.028314        19.248949
SCC                                   8.955943  7.7995747  1.7889535            10.826806         4.665895
FAF                                   5.022509  2.3508167 10.1085902             8.967674        22.528533
TUE                                   6.473534  6.7554121  3.6319446             9.778572        16.398927
NCP_3                                 3.998360  7.5202190  3.7027033             8.320579        11.075586
CALC_3                               -1.310107  4.5777954  9.8395815             5.384242        14.646874
MTRANS_3                              4.197121  4.9049469 -0.1114467             6.348510        10.599633

Interpretation:

Age and height are the most important variables, ranking highest in both accuracy decrease and Gini decrease measures. Water intake (CH2O) and eating frequency (CAEC) are also important, especially for predicting obese cases. Family history of overweight matters more for obese prediction than for normal weight prediction. Physical activity (FAF) is useful for predicting obese cases. Gender and smoking are less important overall. The importance rankings show that demographic factors like age and height are the strongest predictors of obesity, followed by behavioral factors like water intake and eating habits. This matches what the single trees showed—age is always the first split, suggesting it is the strongest dividing factor for obesity classification.

5.2.2 Test Set Performance

pred_bag <- predict(bag_model, newdata = test_data, type = "class")
pred_prob_bag <- predict(bag_model, newdata = test_data, type = "prob")
conf_matrix_bag <- table(Predicted = pred_bag, Actual = test_data$Obesity_3)
print(conf_matrix_bag)
                 Actual
Predicted         Normal_or_Under Overweight Obese
  Normal_or_Under              58         17     5
  Overweight                    4          6     6
  Obese                         2          3     6
total_error_bag <- mean(pred_bag != test_data$Obesity_3)
class_errors_bag <- 1 - diag(conf_matrix_bag) / colSums(conf_matrix_bag)
roc_bag <- multiclass.roc(test_data$Obesity_3, pred_prob_bag)
auc_bag <- auc(roc_bag)

Performance Metrics:

Metric Value
OOB Error Rate (Training) 31.6%
Total Error Rate 34.58%
Normal_or_Under Error 9.38%
Overweight Error 76.92%
Obese Error 64.71%
Multiclass AUC 0.7207

On the test set, bagging has a total error rate of 34.58%, which is slightly worse than the training OOB error of 31.6%, suggesting some overfitting. The model performs well on normal weight people with only 9.38% error, but struggles with overweight (76.92% error) and obese (64.71% error) groups. The multiclass AUC of 0.72 is good and matches the Gini tree’s performance. Compared to single tree models, bagging makes fewer mistakes on obese cases (64.71% vs 82.35% for deviance tree, 52.94% for Gini tree) but has higher error on normal weight cases (9.38% vs 3.12% for deviance tree, 14.06% for Gini tree). Overall, bagging improves predictions for harder-to-classify groups like obese, but the class imbalance issue remains—the model is still better at identifying normal weight people than heavier groups.

5.3 Random Forest Results

set.seed(208)
p <- length(allowed_predictors)
mtry_rf <- floor(sqrt(p))
cat("Number of predictors (p):", p, "\n")
Number of predictors (p): 15 
cat("mtry for Random Forest:  ", mtry_rf, "\n\n")
mtry for Random Forest:   3 
rf_model <- randomForest(
  Obesity_3 ~ ., 
  data = train_data,
  mtry = mtry_rf,
  ntree = 500, 
  importance = TRUE
)
cat("--- Random Forest Model Summary ---\n")
--- Random Forest Model Summary ---
print(rf_model)

Call:
 randomForest(formula = Obesity_3 ~ ., data = train_data, mtry = mtry_rf,      ntree = 500, importance = TRUE) 
               Type of random forest: classification
                     Number of trees: 500
No. of variables tried at each split: 3

        OOB estimate of  error rate: 31.13%
Confusion matrix:
                Normal_or_Under Overweight Obese class.error
Normal_or_Under             247         12     4   0.0608365
Overweight                   68         35     3   0.6698113
Obese                        33         12    10   0.8181818

Random forest was built by using only 3 random variables at each split instead of all 15 variables like in bagging. This forces the model to consider different variables and reduces the correlation between trees, which can improve predictions. The random forest created 500 trees with an OOB error rate of 31.13%, which is slightly better than bagging’s 31.6%. The error rates by class show that random forest performs well on normal weight people (6.08% error), similar to bagging. However, error rates for overweight (66.98% error) and obese (81.82% error) groups are higher than bagging, suggesting that limiting variables to 3 per split makes it harder to distinguish between heavier groups. ### Variable Importance

varImpPlot(rf_model, main = "Random Forest: Variable Importance")

cat("\n--- Variable Importance ---\n")

--- Variable Importance ---
importance(rf_model)
                               Normal_or_Under Overweight     Obese MeanDecreaseAccuracy MeanDecreaseGini
Gender                                9.926345  4.7722536  5.602449            11.991152         7.787860
Age_bin5                             18.727391 14.6128396  9.703711            24.735348        29.728226
Height_bin5                          11.757727  9.9748965  6.209251            16.422311        24.676735
family_history_with_overweight        8.102230  9.9517472 12.362316            14.961899         9.787452
FAVC                                  6.181116  1.7531174  3.827956             7.090257         8.254874
FCVC                                  4.742707  7.9106164  2.645394             8.577288        12.397965
CAEC                                  5.857850 15.7476444  4.821944            15.439690        16.848332
SMOKE                                 7.186437  0.1811882  3.195207             6.566445         5.002481
CH2O                                  8.712491 11.4174832 13.806336            16.712332        16.514889
SCC                                   4.737135  4.0154324  2.713076             6.563664         4.994514
FAF                                   5.595125  5.1553089  9.766466            10.480191        17.623271
TUE                                   6.217586  7.6423821  7.083067            10.721153        13.789949
NCP_3                                 2.943702 12.1303269  4.277942            11.478727        11.514739
CALC_3                                3.528662  6.0691509  8.088388             8.465518        13.374823
MTRANS_3                              3.861899  4.6649152  2.137497             6.011020        11.444024

Interpretation: The variable importance in random forest shows similar patterns to bagging, with age and height as the top factors. However, the rankings change slightly because random forest only considers 3 variables per split. Water intake (CH2O) becomes more important in random forest, suggesting that when age and height are not available in a split, water intake is a useful alternative. Eating frequency (CAEC), family history, and physical activity (FAF) remain important.

5.3.1 Test Set Performance

pred_rf <- predict(rf_model, newdata = test_data, type = "class")
pred_prob_rf <- predict(rf_model, newdata = test_data, type = "prob")
conf_matrix_rf <- table(Predicted = pred_rf, Actual = test_data$Obesity_3)
print(conf_matrix_rf)
                 Actual
Predicted         Normal_or_Under Overweight Obese
  Normal_or_Under              62         19     5
  Overweight                    1          5     6
  Obese                         1          2     6
total_error_rf  <- mean(pred_rf != test_data$Obesity_3)

class_errors_rf <- 1 - diag(conf_matrix_rf) / colSums(conf_matrix_rf)

roc_rf <- multiclass.roc(test_data$Obesity_3, pred_prob_rf)
auc_rf <- auc(roc_rf)

Performance Metrics:

Metric Value
OOB Error Rate (Training) 31.13%
Total Error Rate 31.78%
Normal_or_Under Error 3.12%
Overweight Error 80.77%
Obese Error 64.71%
Multiclass AUC 0.7503

5.4 Model Comparison

5.4.1 Summary Table

summary_df <- data.frame(
  Model = c("Tree (Deviance)", "Tree (Gini)", "Bagging", "Random Forest"),
  Total_Error = c(total_error_dev, total_error_gini, total_error_bag, total_error_rf),
  Normal_Error = c(class_errors_dev["Normal_or_Under"], class_errors_gini["Normal_or_Under"], class_errors_bag["Normal_or_Under"], class_errors_rf["Normal_or_Under"]),
  Overweight_Error = c(class_errors_dev["Overweight"], class_errors_gini["Overweight"], class_errors_bag["Overweight"], class_errors_rf["Overweight"]),
  Obese_Error = c(class_errors_dev["Obese"], class_errors_gini["Obese"], class_errors_bag["Obese"], class_errors_rf["Obese"]),
  AUC = c(auc_dev, auc_gini, auc_bag, auc_rf))
# 4. Format for display
summary_df_print <- summary_df
summary_df_print[, 2:5] <- lapply(summary_df_print[, 2:5], function(x) paste0(round(x * 100, 1), "%"))
summary_df_print$AUC <- round(summary_df$AUC, 4)
print(summary_df_print)

5.4.2 Comparison Visualization

error_matrix <- as.matrix(summary_df[, c("Total_Error", "Normal_Error",                                          "Overweight_Error", "Obese_Error")])
rownames(error_matrix) <- summary_df$Model
col_palette <- c("#D73027", "#FC8D59", "#FEE090", "#91BFDB")
par(mar = c(7, 4, 4, 8), xpd = TRUE)
barplot(t(error_matrix) * 100, beside = TRUE,col = col_palette,main = "Model Error Comparison",ylab = "Error Rate (%)",ylim = c(0, max(error_matrix) * 120),las = 2,cex.names = 0.8)
legend("topright", inset = c(-0.25, 0),legend = c("Total", "Normal", "Overweight", "Obese"),
fill = col_palette,title = "Error Type",cex = 0.8,bty = "n")
par(mar = c(5, 4, 4, 2), xpd = FALSE)
par(mar = c(7, 4, 4, 2))

barplot(summary_df$AUC,names.arg = summary_df$Model,col = c("coral", "orange", "steelblue", "darkblue"),main = "Model AUC Comparison",ylab = "AUC",ylim = c(0, 1),las = 2,cex.names = 0.8)
abline(h = 0.5, lty = 2, col = "darkgray")
par(mar = c(5, 4, 4, 2))

best_idx <- which.min(summary_df$Total_Error)
best_model <- summary_df$Model[best_idx]
cat("\n===== BEST MODEL SELECTION =====\n\n")

===== BEST MODEL SELECTION =====
cat("Lowest Total Error:    ", summary_df$Model[which.min(summary_df$Total_Error)], 
    "(", round(min(summary_df$Total_Error) * 100, 1), "%)\n")
Lowest Total Error:     Random Forest ( 31.8 %)
cat("Highest AUC:           ", summary_df$Model[which.max(summary_df$AUC)], 
    "(", round(max(summary_df$AUC), 4), ")\n")
Highest AUC:            Random Forest ( 0.7503 )
cat("Best Overweight Class: ", summary_df$Model[which.min(summary_df$Overweight_Error)], 
    "(", round(min(summary_df$Overweight_Error) * 100, 1), "%)\n")
Best Overweight Class:  Tree (Gini) ( 69.2 %)
cat("\n>> Selected Best Model:", best_model, "\n")

>> Selected Best Model: Random Forest 

5.4.3 Best Model Selection

Comparing all four models on the test set, random forest performs best with a total error rate of 31.8% and the highest AUC of 0.75. Random forest correctly identifies normal weight people with 3.1% error, the same as the deviance tree. For obese cases, random forest has 64.7% error, matching bagging. The Gini tree performs better on overweight cases (69.2% error) compared to random forest (80.8% error). Overall, random forest achieves the best balance between total accuracy and discrimination between classes, making it the best model for this obesity classification task. The deviance tree is simple and interpretable with 10 nodes, but has the lowest AUC (0.65). The Gini tree is more complex with 23 nodes and performs better on overweight classification but worse overall. Bagging uses all variables and achieves moderate performance with AUC of 0.72. Random forest balances simplicity and performance by using only 3 variables per split, achieving the lowest total error (31.8%) and highest AUC (0.75). The main challenge across all models is classifying overweight and obese cases, as these groups share many characteristics. Demographic factors like age and height are consistently the most important predictors, followed by behavioral factors such as water intake and eating frequency. Random forest is selected as the best model because it offers the best trade-off between accuracy, AUC performance, and generalization to new data.


5.4.4 Bagging vs Random Forest: Direct Comparison

par(mfrow = c(1, 1))

plot(1:500, bag_model$err.rate[, "OOB"], type = "l", col = "red", lwd = 2,ylim = range(c(bag_model$err.rate[, "OOB"], rf_model$err.rate[, "OOB"])),xlab = "Number of Trees", ylab = "OOB Error Rate",main = "Bagging vs Random Forest: OOB Error Convergence")
lines(1:500, rf_model$err.rate[, "OOB"], col = "blue", lwd = 2)
legend("topright", legend = c(paste0("Bagging (mtry=", p, ")"), paste0("Random Forest (mtry=", mtry_rf, ")")),col = c("red", "blue"), lwd = 2,bty = "n")
grid()

The OOB error convergence plot shows how bagging and random forest improve as more trees are added. Both models start with high error rates around 45% when using few trees, then quickly improve as trees are added. Bagging converges to about 31.6% error and random forest converges to about 31.1% error. Random forest achieves lower error with fewer trees initially, suggesting it learns faster and more efficiently. By around 100 trees, both models have stabilized, meaning adding more trees beyond this point provides little improvement. The red line for bagging is slightly higher than the blue line for random forest throughout, showing that random forest’s strategy of considering only 3 random variables per split leads to better final performance. This demonstrates that random forest’s diversity-promoting approach works better than bagging’s approach of using all variables.


6 Discussion

6.1 Key Findings

Ensemble methods like bagging and random forest consistently outperform single decision trees. The deviance tree has a total error rate of 33.6% and AUC of 0.65, while random forest achieves 31.8% error and AUC of 0.75. Bagging and random forest reduce overfitting by combining predictions from many trees built on different data samples. Random forest performs even better than bagging because it adds randomness by considering only 3 variables per split, which reduces correlation between trees and improves diversity. Age and height are the most important predictors across all models. Both demographic factors rank highest in variable importance measures, indicating they are fundamental to obesity classification. Water intake (CH2O) and eating frequency (CAEC) are the next most important, showing that behavioral factors also play a major role. Family history of overweight matters for obese predictions. Gender and smoking are less important. This consistency across different models suggests that these variables capture real patterns in obesity and should be the focus of intervention strategies. Age-related physiological changes and body structure (height) are the primary determinants of obesity risk. All models struggle more with overweight and obese classification compared to normal weight classification. The deviance tree has only 3.1% error for normal weight but 76.9% error for overweight and 82.4% for obese. This imbalance occurs because overweight and obese people share similar characteristics, making them hard to distinguish. The overweight class also has fewer samples (132 vs 327 normal weight), creating a class imbalance problem. Random forest reduces obese error to 64.7% but overweight remains difficult at 80.8%. The Gini tree performs best on overweight (69.2% error) because its larger size allows more complex decision boundaries.


6.2 Variable Importance Interpretation

Age and height are the strongest predictors of obesity, consistently ranking first and second across all models. Age matters because obesity risk changes with life stage—younger people tend to have different metabolic rates and lifestyles than older adults. Height is important because BMI and obesity classifications depend on body measurements relative to height. Together, these demographic factors explain most of the variation in obesity classification. Water intake (CH2O) is the third most important behavioral factor. People who drink more water have lower obesity risk, likely because water replaces high-calorie beverages and helps with satiety. Eating frequency outside the home (CAEC) is also important, suggesting that eating patterns and where people eat affects obesity. Physical activity (FAF) is important for obese classification, showing that exercise is a key factor in distinguishing obese people from other groups. Gender, smoking, and transportation method have lower importance scores. Gender shows some effect but is less predictive than age and height. Smoking has minimal importance, suggesting it is not a strong obesity indicator in this dataset. Transportation method (MTRANS_3) ranks lowest, meaning whether people walk, use cars, or use public transport has little effect on obesity prediction after accounting for other factors. The number of main meals (NCP_3) and caloric beverage consumption (CALC_3) show moderate importance but are weaker than the top predictors.

7 Conclusions

7.1 Summary

This analysis compared four tree-based models for obesity classification: single decision trees using deviance and Gini splits, bagging, and random forest. Random forest emerged as the best model with 31.8% total error and AUC of 0.75. Age and height are the strongest predictors of obesity, followed by behavioral factors like water intake and eating frequency. Ensemble methods significantly outperform single trees by reducing overfitting and combining diverse predictions. The main challenge is classifying overweight cases due to overlap with normal and obese groups and smaller sample size. Random forest balances accuracy and efficiency by using only 3 random variables per split, achieving faster convergence than bagging while maintaining interpretability of variable importance.


8 Disclaimer

1- This analysis is for educational and research purposes only. The models developed here should not be used for clinical diagnosis or medical decision-making without validation by qualified healthcare professionals. Obesity is a complex condition influenced by genetic, environmental, behavioral, and medical factors not fully captured by the available data. Individual predictions from these models may not accurately reflect actual obesity risk. The dataset is primarily synthetic and may not represent real populations. Results should not be generalized beyond the specific population and features studied. Healthcare providers should use their clinical judgment and evidence-based guidelines for obesity assessment and treatment, not rely solely on machine learning predictions. This analysis assumes data quality and completeness without extensive data validation. Users of these models assume all responsibility for appropriate application and interpretation of results. 2- This analysis uses three elements not covered in MQT7015 labs: pROC Package: The course uses the ROCR package for ROC analysis, which only supports binary classification. Since our target variable has 3 classes (Normal_or_Under, Overweight, Obese), we used the pROC package’s multiclass.roc() function to compute AUC for multi-class problems. ggplot2 Package: Listed for potential visualization but minimally used.

9 References

[1] Mendoza Palechor, F., & de la Hoz Manotas, A. (2019). Dataset for estimation of obesity levels based on eating habits and physical condition in individuals from Colombia, Peru and Mexico. Data in Brief, 25, 104344. https://doi.org/10.1016/j.dib.2019.104344 [2] Cremona, M. A., & Severino, F. (2024). MQT7015-Lab6: Arbres de décision [Decision Trees Lab]. Course materials for MQT7015. [3] Cremona, M. A., & Severino, F. (2024). MQT7015-Lab7: Forêts aléatoires et agrégation des modèles [Random Forests and Model Aggregation Lab]. Course materials for MQT7015.

LS0tCnRpdGxlOiAiUHJlZGljdGluZyBPYmVzaXR5IFJpc2sgZnJvbSBCZWhhdmlvcmFsIGFuZCBEZW1vZ3JhcGhpYyBGYWN0b3JzOiBBIENvbXBhcmF0aXZlCiAgQW5hbHlzaXMgb2YgVHJlZS1CYXNlZCBNYWNoaW5lIExlYXJuaW5nIE1ldGhvZHMiCmF1dGhvcjogIkdoYWZvdXJpYW5OYXNpcmlfTW9oYW1tYWQiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogJzMnCiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgcGRmX2RvY3VtZW50OgogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CiAgICB0b2M6IHRydWUgICAgICAgICMgaWYgeW91IHdhbnQgVE9DCiAgICB0b2NfZGVwdGg6IDMgICAgICMgb3B0aW9uYWwKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nID0gRkFMU0UsZmlnLmFsaWduID0gImNlbnRlciIsZmlnLndpZHRoID0gMTAsZmlnLmhlaWdodCA9IDYpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEFic3RyYWN0IHsudW5udW1iZXJlZH0KCk9iZXNpdHkgaXMgYSBncm93aW5nIGdsb2JhbCBoZWFsdGggY2hhbGxlbmdlLCBhbmQgdGhpcyBzdHVkeSBhaW1zIHRvIHByZWRpY3Qgb2Jlc2l0eSBsZXZlbHMgYmFzZWQgc29sZWx5IG9uIGxpZmVzdHlsZSBhbmQgZWF0aW5nIGhhYml0cy4gVGhpcyBzdHVkeSBkZXZlbG9wcyBhbmQgY29tcGFyZXMgZm91ciB0cmVlLWJhc2VkIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzIHRvIHByZWRpY3Qgb2Jlc2l0eSBjbGFzc2lmaWNhdGlvbiBmcm9tIGRlbW9ncmFwaGljIGFuZCBiZWhhdmlvcmFsIGRhdGEuIE1vZGVscyBldmFsdWF0ZWQgaW5jbHVkZSBzaW5nbGUgZGVjaXNpb24gdHJlZXMgdXNpbmcgZGV2aWFuY2UgYW5kIEdpbmkgc3BsaXR0aW5nIGNyaXRlcmlhLCBiYWdnaW5nLCBhbmQgcmFuZG9tIGZvcmVzdC4gVGhlIGRhdGFzZXQgY29udGFpbnMgNTMxIG9ic2VydmF0aW9ucyB3aXRoIDE1IHByZWRpY3RvciB2YXJpYWJsZXMgaW5jbHVkaW5nIGFnZSwgaGVpZ2h0LCB3YXRlciBpbnRha2UsIGVhdGluZyBmcmVxdWVuY3ksIHBoeXNpY2FsIGFjdGl2aXR5LCBhbmQgZmFtaWx5IGhpc3RvcnkuIFJhbmRvbSBmb3Jlc3QgZW1lcmdlZCBhcyB0aGUgYmVzdCBtb2RlbCB3aXRoIDMxLjglIHRvdGFsIGVycm9yIGFuZCBBVUMgb2YgMC43NS4gQWdlIGFuZCBoZWlnaHQgYXJlIHRoZSBzdHJvbmdlc3QgcHJlZGljdG9ycyBvZiBvYmVzaXR5LCBmb2xsb3dlZCBieSBiZWhhdmlvcmFsIGZhY3RvcnMgbGlrZSB3YXRlciBpbnRha2UgYW5kIGVhdGluZyBmcmVxdWVuY3kuIEVuc2VtYmxlIG1ldGhvZHMgc2lnbmlmaWNhbnRseSBvdXRwZXJmb3JtIHNpbmdsZSB0cmVlcywgd2l0aCByYW5kb20gZm9yZXN0IGFjaGlldmluZyAzMS44JSBlcnJvciBjb21wYXJlZCB0byAzMy42JSBmb3Igc2luZ2xlIGRlY2lzaW9uIHRyZWVzLiBUaGUgbWFpbiBjaGFsbGVuZ2UgaXMgY2xhc3NpZnlpbmcgb3ZlcndlaWdodCBjYXNlcyBkdWUgdG8gY2xhc3MgaW1iYWxhbmNlIGFuZCBvdmVybGFwIHdpdGggb3RoZXIgb2Jlc2l0eSBjYXRlZ29yaWVzLiBSZXN1bHRzIHNob3cgdGhhdCBkZW1vZ3JhcGhpYyBhbmQgbGlmZXN0eWxlIGZhY3RvcnMgY2FuIHByZWRpY3Qgb2Jlc2l0eSByZWFzb25hYmx5IHdlbGwsIGJ1dCBwcmVkaWN0aW9uIGFjY3VyYWN5IHZhcmllcyBhY3Jvc3Mgb2Jlc2l0eSBjbGFzc2VzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIEludHJvZHVjdGlvbgoKIyMgQmFja2dyb3VuZCBhbmQgTW90aXZhdGlvbgoKT2Jlc2l0eSBpcyBhIG1ham9yIHB1YmxpYyBoZWFsdGggY2hhbGxlbmdlLiBUaGUgV29ybGQgSGVhbHRoIE9yZ2FuaXphdGlvbiAoV0hPKSByZXBvcnRzIHRoYXQgZ2xvYmFsIG9iZXNpdHkgcHJldmFsZW5jZSBoYXMgbmVhcmx5IHRyaXBsZWQgc2luY2UgMTk3NS4gQ3VycmVudCBkYXRhIGluZGljYXRlcyB0aGF0IG92ZXIgb25lIGJpbGxpb24gcGVvcGxlIGxpdmUgd2l0aCBvYmVzaXR5LCBpbmNsdWRpbmcgYXBwcm94aW1hdGVseSA4OTAgbWlsbGlvbiBhZHVsdHMgYW5kIDE2MCBtaWxsaW9uIGNoaWxkcmVuIGFuZCBhZG9sZXNjZW50cy4gSW5jaWRlbmNlIHJhdGVzIGFyZSByaXNpbmcgaW4gbG93LSBhbmQgbWlkZGxlLWluY29tZSBjb3VudHJpZXMsIHdoZXJlIGhlYWx0aGNhcmUgc3lzdGVtcyBvZnRlbiBmYWNlIGEgZHVhbCBidXJkZW4gb2YgbWFsbnV0cml0aW9uIGFuZCBtZXRhYm9saWMgZGlzZWFzZS4gVGhpcyBjb25kaXRpb24gZHJpdmVzIG5vbi1jb21tdW5pY2FibGUgZGlzZWFzZXMsIGluY2x1ZGluZyB0eXBlIDIgZGlhYmV0ZXMsIGNhcmRpb3Zhc2N1bGFyIGRpc2Vhc2UsIGFuZCBjYW5jZXIsIHRocm91Z2ggbWVjaGFuaXNtcyBzdWNoIGFzIHN5c3RlbWljIGluZmxhbW1hdGlvbiBhbmQgaW5zdWxpbiByZXNpc3RhbmNlLiBUaGUgZWNvbm9taWMgaW1wYWN0IG9mIG9iZXNpdHkgaXMgc3Vic3RhbnRpYWwuIFRoZSBXb3JsZCBPYmVzaXR5IEZlZGVyYXRpb24gcHJvamVjdHMgdGhhdCB0aGUgZ2xvYmFsIGNvc3Qgb2Ygb3ZlcndlaWdodCBhbmQgb2Jlc2l0eSB3aWxsIHJlYWNoIFwkNC4zMiB0cmlsbGlvbiBhbm51YWxseSBieSAyMDM1LCBlcXVpdmFsZW50IHRvIG5lYXJseSAzJSBvZiBnbG9iYWwgR0RQLiBJbmRpcmVjdCBjb3N0cywgc3RlbW1pbmcgZnJvbSBsb3N0IHdvcmtmb3JjZSBwcm9kdWN0aXZpdHksIGFic2VudGVlaXNtLCBhbmQgcHJlbWF0dXJlIG1vcnRhbGl0eSwgb2Z0ZW4gZXhjZWVkIGRpcmVjdCBtZWRpY2FsIGV4cGVuZGl0dXJlcy4gQ29uc2VxdWVudGx5LCB0aGUgZmFpbHVyZSB0byBhZGRyZXNzIG9iZXNpdHkgYWZmZWN0cyBuYXRpb25hbCBlY29ub21pZXMgYWxvbmdzaWRlIGhlYWx0aGNhcmUgc3lzdGVtcy4gQWRkcmVzc2luZyB0aGlzIGNoYWxsZW5nZSByZXF1aXJlcyBtb3ZpbmcgZnJvbSByZWFjdGl2ZSBtYW5hZ2VtZW50IHRvIGVhcmx5IGlkZW50aWZpY2F0aW9uLiBDdXJyZW50IHNjcmVlbmluZyByZWxpZXMgb24gQm9keSBNYXNzIEluZGV4IChCTUkpLCBhIGxhZ2dpbmcgaW5kaWNhdG9yIHRoYXQgbWF5IG5vdCBjYXB0dXJlIGJvZHkgY29tcG9zaXRpb24gbnVhbmNlcyBvciBpZGVudGlmeSByaXNrIGJlZm9yZSB3ZWlnaHQgZ2FpbiBvY2N1cnMuIFByZWRpY3RpdmUgdG9vbHMgdGhhdCBpZGVudGlmeSBhdC1yaXNrIGluZGl2aWR1YWxzIGJhc2VkIG9uIG1vZGlmaWFibGUgYmVoYXZpb3JzLCBzdWNoIGFzIGRpZXQgYW5kIHBoeXNpY2FsIGFjdGl2aXR5LCBhcmUgbmVjZXNzYXJ5LiBUaGlzIHN0dWR5IGFkZHJlc3NlcyB0aGlzIGdhcCBieSBkZXZlbG9waW5nIGFuZCBiZW5jaG1hcmtpbmcgbWFjaGluZSBsZWFybmluZyBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgdG8gcHJlZGljdCBvYmVzaXR5IHN0YXR1cyB1c2luZyBkZW1vZ3JhcGhpYyBhbmQgbGlmZXN0eWxlIGNoYXJhY3RlcmlzdGljcy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRGF0YXNldCBEZXNjcmlwdGlvbgoKIyMjIERhdGEgU291cmNlCgpUaGUgZGF0YXNldCBvcmlnaW5hdGVzIGZyb20gdGhlIHN0dWR5IGJ5IFBhbGVjaG9yIGFuZCBkZSBsYSBIb3ogTWFub3RhcyAoMjAxOSksIHRpdGxlZCAqIkRhdGFzZXQgZm9yIGVzdGltYXRpb24gb2Ygb2Jlc2l0eSBsZXZlbHMgYmFzZWQgb24gZWF0aW5nIGhhYml0cyBhbmQgcGh5c2ljYWwgY29uZGl0aW9uIGluIGluZGl2aWR1YWxzIGZyb20gQ29sb21iaWEsIFBlcnUgYW5kIE1leGljby4iKiBEYXRhIHdhcyBjb2xsZWN0ZWQgdmlhIGFuIG9ubGluZSBxdWVzdGlvbm5haXJlIGFkbWluaXN0ZXJlZCB0byBpbmRpdmlkdWFscyBhZ2VkIDE0LTYxIHllYXJzIHJlc2lkaW5nIGluIENvbG9tYmlhLCBQZXJ1LCBhbmQgTWV4aWNvLiBUaGUgb3JpZ2luYWwgZGF0YXNldCBjb250YWlucyAyLDExMSBvYnNlcnZhdGlvbnMuIEhvd2V2ZXIsIGFwcHJveGltYXRlbHkgNzUlIG9mIHRoZXNlIG9ic2VydmF0aW9ucyB3ZXJlIHN5bnRoZXRpY2FsbHkgZ2VuZXJhdGVkIHVzaW5nIFNNT1RFIChTeW50aGV0aWMgTWlub3JpdHkgT3Zlci1zYW1wbGluZyBUZWNobmlxdWUpIHRvIGJhbGFuY2UgdGhlIG9iZXNpdHkgY2xhc3Nlcy4gVGhpcyBzeW50aGV0aWMgYXVnbWVudGF0aW9uIGlzIGEgY3JpdGljYWwgY29uc2lkZXJhdGlvbiBhZGRyZXNzZWQgaW4gb3VyIGRhdGEgcHJlcGFyYXRpb24uCgojIyMgVmFyaWFibGUgRGVzY3JpcHRpb25zCgpUaGUgZm9sbG93aW5nIHRhYmxlIGRlc2NyaWJlcyBhbGwgdmFyaWFibGVzIGluIHRoZSBkYXRhc2V0OgoKfCBWYXJpYWJsZSB8IFR5cGUgfCBEZXNjcmlwdGlvbiB8IExldmVscy9Vbml0cyB8Cnw6LS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS18CnwgKipEZW1vZ3JhcGhpYyBWYXJpYWJsZXMqKiB8ICB8ICB8ICB8CnwgR2VuZGVyIHwgQ2F0ZWdvcmljYWwgfCBCaW9sb2dpY2FsIHNleCB8IEZlbWFsZSwgTWFsZSB8CnwgQWdlIHwgTnVtZXJpYyB8IEFnZSBpbiB5ZWFycyB8IDE0LTYxIHllYXJzIHwKfCBIZWlnaHQgfCBOdW1lcmljIHwgSGVpZ2h0IHwgTWV0ZXJzIHwKfCBXZWlnaHQgfCBOdW1lcmljIHwgV2VpZ2h0IHwgS2lsb2dyYW1zIHwKfCAqKkVhdGluZyBIYWJpdHMqKiB8ICB8ICB8ICB8CnwgRkFWQyB8IEJpbmFyeSB8IEZyZXF1ZW50IGNvbnN1bXB0aW9uIG9mIGhpZ2gtY2Fsb3JpZSBmb29kIHwgTm8sIFllcyB8CnwgRkNWQyB8IE9yZGluYWwgfCBGcmVxdWVuY3kgb2YgdmVnZXRhYmxlIGNvbnN1bXB0aW9uIHwgMT1OZXZlciwgMj1Tb21ldGltZXMsIDM9QWx3YXlzIHwKfCBOQ1AgfCBPcmRpbmFsIHwgTnVtYmVyIG9mIG1haW4gbWVhbHMgcGVyIGRheSB8IDEsIDIsIDMsIDQrIHwKfCBDQUVDIHwgQ2F0ZWdvcmljYWwgfCBDb25zdW1wdGlvbiBvZiBmb29kIGJldHdlZW4gbWVhbHMgfCBObywgU29tZXRpbWVzLCBGcmVxdWVudGx5LCBBbHdheXMgfAp8IENIMk8gfCBPcmRpbmFsIHwgRGFpbHkgd2F0ZXIgY29uc3VtcHRpb24gfCAxPUxvdywgMj1NZWRpdW0sIDM9SGlnaCB8CnwgQ0FMQyB8IENhdGVnb3JpY2FsIHwgQWxjb2hvbCBjb25zdW1wdGlvbiBmcmVxdWVuY3kgfCBObywgU29tZXRpbWVzLCBGcmVxdWVudGx5LCBBbHdheXMgfAp8ICoqTGlmZXN0eWxlIFZhcmlhYmxlcyoqIHwgIHwgIHwgIHwKfCBTTU9LRSB8IEJpbmFyeSB8IFNtb2tpbmcgc3RhdHVzIHwgTm8sIFllcyB8CnwgU0NDIHwgQmluYXJ5IHwgQ2Fsb3JpZSBjb25zdW1wdGlvbiBtb25pdG9yaW5nIHwgTm8sIFllcyB8CnwgRkFGIHwgT3JkaW5hbCB8IFBoeXNpY2FsIGFjdGl2aXR5IGZyZXF1ZW5jeSB8IDA9Tm9uZSwgMT0xLTIgZGF5cywgMj0yLTQgZGF5cywgMz00KyBkYXlzIHwKfCBUVUUgfCBPcmRpbmFsIHwgVGltZSB1c2luZyB0ZWNobm9sb2d5IGRldmljZXMgfCAwPTAtM2gsIDE9My01aCwgMj01aCsgfAp8IE1UUkFOUyB8IENhdGVnb3JpY2FsIHwgVHJhbnNwb3J0YXRpb24gdXNlZCB8IEF1dG9tb2JpbGUsIEJpa2UsIE1vdG9yYmlrZSwgUHVibGljIFRyYW5zcG9ydCwgV2Fsa2luZyB8CnwgZmFtaWx5X2hpc3Rvcnlfd2l0aF9vdmVyd2VpZ2h0IHwgQmluYXJ5IHwgRmFtaWx5IGhpc3Rvcnkgb2Ygb3ZlcndlaWdodCB8IE5vLCBZZXMgfAp8ICoqVGFyZ2V0IFZhcmlhYmxlKiogfCAgfCAgfCAgfAp8IE5PYmV5ZXNkYWQgfCBDYXRlZ29yaWNhbCB8IE9iZXNpdHkgbGV2ZWwgY2xhc3NpZmljYXRpb24gfCA3IGxldmVscyAoSW5zdWZmaWNpZW50IFdlaWdodCB0byBPYmVzaXR5IFR5cGUgSUlJKSB8CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFJlc2VhcmNoIFF1ZXN0aW9ucwoKVGhpcyBhbmFseXNpcyBhZGRyZXNzZXMgdGhlIGZvbGxvd2luZyByZXNlYXJjaCBxdWVzdGlvbjoKCldoYXQgZGVtb2dyYXBoaWMgYW5kIGJlaGF2aW9yYWwgZmFjdG9ycyBiZXN0IHByZWRpY3Qgb2Jlc2l0eSByaXNrLCBhbmQgd2hpY2ggbWFjaGluZSBsZWFybmluZyBtZXRob2QgcHJvdmlkZXMgdGhlIG1vc3QgYWNjdXJhdGUgY2xhc3NpZmljYXRpb24/CgojIERhdGEgUHJlcGFyYXRpb24KCmBgYHtyIGxvYWQtbGlicmFyaWVzfQpsaWJyYXJ5KHRyZWUpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQpzZXQuc2VlZCgxMjMpICAKYGBgCgojIyBSZWFsIHZzLiBTeW50aGV0aWMgRGF0YSBTZXBhcmF0aW9uCgojIyMgUmF0aW9uYWxlCgpUaGUgb3JpZ2luYWwgZGF0YXNldCBjb250YWlucyBib3RoIHJlYWwgc3VydmV5IHJlc3BvbnNlcyBhbmQgc3ludGhldGljYWxseSBnZW5lcmF0ZWQgb2JzZXJ2YXRpb25zICh2aWEgU01PVEUpLiBVc2luZyBzeW50aGV0aWMgZGF0YSBtYXkgaW50cm9kdWNlIGFydGlmYWN0cyB0aGF0IGRvIG5vdCByZWZsZWN0IGh1bWFuIGJlaGF2aW9yIHBhdHRlcm5zLiBUbyBlbnN1cmUgb3VyIGZpbmRpbmdzIGFyZSB2YWxpZCBhbmQgZ2VuZXJhbGl6YWJsZSwgd2UgaWRlbnRpZnkgYW5kIGZpbHRlciBvdXQgc3ludGhldGljIG9ic2VydmF0aW9ucy4KCioqSWRlbnRpZmljYXRpb24gTWV0aG9kOioqIFJlYWwgc3VydmV5IHJlc3BvbnNlcyBoYXZlIGludGVnZXIgdmFsdWVzIGZvciB2YXJpYWJsZXMgRkNWQywgTkNQLCBDSDJPLCBGQUYsIGFuZCBUVUUsIHdoaWxlIFNNT1RFLWdlbmVyYXRlZCByZWNvcmRzIGNvbnRhaW4gZGVjaW1hbCB2YWx1ZXMgZHVlIHRvIHRoZSBpbnRlcnBvbGF0aW9uIHByb2Nlc3MuCgpgYGB7ciBsb2FkLWFuZC1maWx0ZXItZGF0YX0KCm9iZXNpdHkgPC0gcmVhZC5jc3YoIk9iZXNpdHlEYXRhU2V0X3Jhd19hbmRfZGF0YV9zaW50aGV0aWMuY3N2IikKaXNfZGVjaW1hbCA8LSBmdW5jdGlvbih4KSB7IHJldHVybigoeCAlJSAxKSAhPSAwKX0Kb2Jlc2l0eSRkYXRhX3R5cGUgPC0gIlJlYWwiCnN5bnRoZXRpY19pbmRpY2VzIDwtIHdoaWNoKCBpc19kZWNpbWFsKG9iZXNpdHkkRkNWQykgfGlzX2RlY2ltYWwob2Jlc2l0eSROQ1ApIHwgaXNfZGVjaW1hbChvYmVzaXR5JENIMk8pIHwgaXNfZGVjaW1hbChvYmVzaXR5JEZBRikgfCBpc19kZWNpbWFsKG9iZXNpdHkkVFVFKSkKb2Jlc2l0eSRkYXRhX3R5cGVbc3ludGhldGljX2luZGljZXNdIDwtICJTeW50aGV0aWMiCnRhYmxlKG9iZXNpdHkkZGF0YV90eXBlKQpwcm9wLnRhYmxlKHRhYmxlKG9iZXNpdHkkZGF0YV90eXBlKSkgKiAxMDAKb2Jlc2l0eV9yZWFsIDwtIG9iZXNpdHlbb2Jlc2l0eSRkYXRhX3R5cGUgPT0gIlJlYWwiLCBdCmBgYAoKIyMjIEludGVycHJldGF0aW9uCgpXZSBwcm9jZWVkIHdpdGggb25seSB0aGUgNTMxIHJlYWwgb2JzZXJ2YXRpb25zIHRvIGVuc3VyZSB2YWxpZGl0eSBvZiBmaW5kaW5ncy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgVmFyaWFibGUgVHJhbnNmb3JtYXRpb25zCgojIyMgRmFjdG9yIENvbnZlcnNpb25zCgpBbGwgY2F0ZWdvcmljYWwgdmFyaWFibGVzIG11c3QgYmUgcHJvcGVybHkgY29udmVydGVkIHRvIFIgZmFjdG9ycyBmb3IgbW9kZWxpbmcuIFRoaXMgZW5zdXJlcyBjb3JyZWN0IHRyZWF0bWVudCBpbiB0cmVlLWJhc2VkIG1ldGhvZHMuCgpgYGB7ciBmYWN0b3ItY29udmVyc2lvbnN9CiMgQ29udmVydCBiaW5hcnkgdmFyaWFibGVzOiBGQVZDLCBTTU9LRSwgU0NDLCBmYW1pbHlfaGlzdG9yeSwgR2VuZGVyCm9iZXNpdHlfcmVhbCRGQVZDICA8LSBmYWN0b3Iob2Jlc2l0eV9yZWFsJEZBVkMsICBsZXZlbHMgPSBjKCJubyIsICJ5ZXMiKSkKb2Jlc2l0eV9yZWFsJFNNT0tFIDwtIGZhY3RvcihvYmVzaXR5X3JlYWwkU01PS0UsIGxldmVscyA9IGMoIm5vIiwgInllcyIpKQpvYmVzaXR5X3JlYWwkU0NDICAgPC0gZmFjdG9yKG9iZXNpdHlfcmVhbCRTQ0MsICAgbGV2ZWxzID0gYygibm8iLCAieWVzIikpCm9iZXNpdHlfcmVhbCRmYW1pbHlfaGlzdG9yeV93aXRoX292ZXJ3ZWlnaHQgPC0gZmFjdG9yKG9iZXNpdHlfcmVhbCRmYW1pbHlfaGlzdG9yeV93aXRoX292ZXJ3ZWlnaHQsbGV2ZWxzID0gYygibm8iLCAieWVzIikpCm9iZXNpdHlfcmVhbCRHZW5kZXIgPC0gZmFjdG9yKG9iZXNpdHlfcmVhbCRHZW5kZXIsIGxldmVscyA9IGMoIkZlbWFsZSIsICJNYWxlIikpCiMgQ29udmVydCBvcmRpbmFsIHZhcmlhYmxlczogRkNWQywgQ0gyTywgRkFGLCBUVUUKb2Jlc2l0eV9yZWFsJEZDVkMgPC0gZmFjdG9yKG9iZXNpdHlfcmVhbCRGQ1ZDLCBsZXZlbHMgPSBjKDEsIDIsIDMpLCBvcmRlcmVkID0gVFJVRSkKb2Jlc2l0eV9yZWFsJENIMk8gPC0gZmFjdG9yKG9iZXNpdHlfcmVhbCRDSDJPLCBsZXZlbHMgPSBjKDEsIDIsIDMpLCBvcmRlcmVkID0gVFJVRSkKb2Jlc2l0eV9yZWFsJEZBRiA8LSBmYWN0b3Iob2Jlc2l0eV9yZWFsJEZBRiwgbGV2ZWxzID0gYygwLCAxLCAyLCAzKSwgb3JkZXJlZCA9IFRSVUUpCm9iZXNpdHkkTkNQICA8LSBmYWN0b3Iob2Jlc2l0eSROQ1AsICBsZXZlbHMgPSBjKDEsIDIsIDMsIDQpKQpvYmVzaXR5X3JlYWwkVFVFIDwtIGZhY3RvcihvYmVzaXR5X3JlYWwkVFVFLCBsZXZlbHMgPSBjKDAsIDEsIDIpLCBvcmRlcmVkID0gVFJVRSkKIyBDb252ZXJ0IG5vbWluYWwgdmFyaWFibGVzOiBDQUVDLCBNVFJBTlMsIE5PYmV5ZXNkYWQKb2Jlc2l0eV9yZWFsJENBRUMgPC0gZmFjdG9yKG9iZXNpdHlfcmVhbCRDQUVDLCBsZXZlbHMgPSBjKCJubyIsICJTb21ldGltZXMiLCAiRnJlcXVlbnRseSIsICJBbHdheXMiKSkKb2Jlc2l0eSRDQUxDIDwtIGZhY3RvcihvYmVzaXR5JENBTEMsbGV2ZWxzID0gYygibm8iLCAiU29tZXRpbWVzIiwgIkZyZXF1ZW50bHkiLCAiQWx3YXlzIikpCm9iZXNpdHkkTVRSQU5TIDwtIGZhY3RvcihvYmVzaXR5JE1UUkFOUywgbGV2ZWxzID0gYygiQXV0b21vYmlsZSIsICJCaWtlIiwgIk1vdG9yYmlrZSIsICJQdWJsaWNfVHJhbnNwb3J0YXRpb24iLCAiV2Fsa2luZyIpKQpvYmVzaXR5JE5PYmV5ZXNkYWQgPC0gZmFjdG9yKG9iZXNpdHkkTk9iZXllc2RhZCxsZXZlbHM9YygiSW5zdWZmaWNpZW50X1dlaWdodCIsIk5vcm1hbF9XZWlnaHQiLCJPdmVyd2VpZ2h0X0xldmVsX0kiLCJPdmVyd2VpZ2h0X0xldmVsX0lJIiwiT2Jlc2l0eV9UeXBlX0kiLCJPYmVzaXR5X1R5cGVfSUkiLCJPYmVzaXR5X1R5cGVfSUlJIikpCnN0cihvYmVzaXR5X3JlYWwpCmBgYAoKIyMjIE1lcmdpbmcgUmFyZSBGYWN0b3IgTGV2ZWxzCgpTZXZlcmFsIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBjb250YWluIGxldmVscyB3aXRoIHZlcnkgZmV3IG9ic2VydmF0aW9ucywgd2hpY2ggY2FuIGNhdXNlIG1vZGVsaW5nIGlzc3Vlcy4gV2UgbWVyZ2UgdGhlc2UgaW50byBtZWFuaW5nZnVsIGdyb3VwZWQgY2F0ZWdvcmllcy4KCiMjIyMgVHJhbnNwb3J0YXRpb24gTW9kZSAoTVRSQU5TKQoKYGBge3IgbWVyZ2UtbXRyYW5zfQpjYXQoIk9yaWdpbmFsIE1UUkFOUyBDb3VudHM6XG4iKQpwcmludCh0YWJsZShvYmVzaXR5X3JlYWwkTVRSQU5TKSkKb2Jlc2l0eV9yZWFsJE1UUkFOU18zIDwtIGFzLmNoYXJhY3RlcihvYmVzaXR5X3JlYWwkTVRSQU5TKQpvYmVzaXR5X3JlYWwkTVRSQU5TXzNbb2Jlc2l0eV9yZWFsJE1UUkFOUyAlaW4lIGMoIkJpa2UiLCAiV2Fsa2luZyIpXSA8LSAiQWN0aXZlIgpvYmVzaXR5X3JlYWwkTVRSQU5TXzNbb2Jlc2l0eV9yZWFsJE1UUkFOUyAlaW4lIGMoIkF1dG9tb2JpbGUiLCAiTW90b3JiaWtlIildIDwtICJQcml2YXRlX01vdG9yIgpvYmVzaXR5X3JlYWwkTVRSQU5TXzNbb2Jlc2l0eV9yZWFsJE1UUkFOUyA9PSAiUHVibGljX1RyYW5zcG9ydGF0aW9uIl0gPC0gIlB1YmxpY19UcmFuc3BvcnQiCm9iZXNpdHlfcmVhbCRNVFJBTlNfMyA8LSBmYWN0b3Iob2Jlc2l0eV9yZWFsJE1UUkFOU18zLCBsZXZlbHMgPSBjKCJBY3RpdmUiLCAiUHJpdmF0ZV9Nb3RvciIsICJQdWJsaWNfVHJhbnNwb3J0IikpCmNhdCgiXG5NZXJnZWQgTVRSQU5TXzMgQ291bnRzOlxuIikKcHJpbnQodGFibGUob2Jlc2l0eV9yZWFsJE1UUkFOU18zKSkKYGBgCgoqKlJhdGlvbmFsZToqKiBUaGUgY2F0ZWdvcmllcyAiQmlrZSIgYW5kICJNb3RvcmJpa2UiIGhhZCBleHRyZW1lbHkgbG93IGZyZXF1ZW5jaWVzLCBsZWFkaW5nIHRvIHNwYXJzaXR5LiBUbyBhZGRyZXNzIHRoaXMsIHdlIGdyb3VwZWQgY2F0ZWdvcmllcyBiYXNlZCBvbiBwaHlzaWNhbCBleGVydGlvbjogIkFjdGl2ZSIgKFdhbGtpbmcgYW5kIEJpa2UpIGFuZCAiUHJpdmF0ZSBNb3RvciIgKEF1dG9tb2JpbGUgYW5kIE1vdG9yYmlrZSkuCgojIyMjIE51bWJlciBvZiBNZWFscyAoTkNQKQoKYGBge3IgbWVyZ2UtbmNwfQpjYXQoIk9yaWdpbmFsIE5DUCBDb3VudHM6XG4iKQpwcmludCh0YWJsZShvYmVzaXR5X3JlYWwkTkNQKSkKb2Jlc2l0eV9yZWFsJE5DUF8zIDwtIE5BX2NoYXJhY3Rlcl8KbmNwX251bWVyaWMgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIob2Jlc2l0eV9yZWFsJE5DUCkpICMgRW5zdXJlIHdlIHRyZWF0IGl0IGFzIG51bWJlciBmb3IgbG9naWMKb2Jlc2l0eV9yZWFsJE5DUF8zW25jcF9udW1lcmljIDw9IDJdIDwtICJMb3dfMS0yIgpvYmVzaXR5X3JlYWwkTkNQXzNbbmNwX251bWVyaWMgPT0gM10gPC0gIk5vcm1hbF8zIgpvYmVzaXR5X3JlYWwkTkNQXzNbbmNwX251bWVyaWMgPj0gNF0gPC0gIkhpZ2hfNCsiCm9iZXNpdHlfcmVhbCROQ1BfMyA8LSBmYWN0b3Iob2Jlc2l0eV9yZWFsJE5DUF8zLCBsZXZlbHMgPSBjKCJMb3dfMS0yIiwgIk5vcm1hbF8zIiwgIkhpZ2hfNCsiKSkKY2F0KCJcbk1lcmdlZCBOQ1BfMyBDb3VudHM6XG4iKQpwcmludCh0YWJsZShvYmVzaXR5X3JlYWwkTkNQXzMpKQpgYGAKCioqUmF0aW9uYWxlOioqIE1vc3QgcGVvcGxlIGNvbnN1bWUgMyBtZWFscyBhIGRheSAoTm9ybWFsKS4gVGhvc2UgZWF0aW5nIDEgb3IgMiBtZWFscyBsaWtlbHkgaGF2ZSBjYWxvcmljIGRlZmljaXRzIG9yIHNraXBwaW5nIGhhYml0cywgd2hpbGUgNCsgc3VnZ2VzdHMgZnJlcXVlbnQgZWF0aW5nLgoKIyMjIyBBbGNvaG9sIENvbnN1bXB0aW9uIChDQUxDKQoKYGBge3IgbWVyZ2UtY2FsY30KcHJpbnQodGFibGUob2Jlc2l0eV9yZWFsJENBTEMpKQpvYmVzaXR5X3JlYWwkQ0FMQ18zIDwtIGFzLmNoYXJhY3RlcihvYmVzaXR5X3JlYWwkQ0FMQykKb2Jlc2l0eV9yZWFsJENBTENfM1tvYmVzaXR5X3JlYWwkQ0FMQyA9PSAibm8iXSA8LSAiTm8iCm9iZXNpdHlfcmVhbCRDQUxDXzNbb2Jlc2l0eV9yZWFsJENBTEMgPT0gIlNvbWV0aW1lcyJdIDwtICJTb21ldGltZXMiCm9iZXNpdHlfcmVhbCRDQUxDXzNbb2Jlc2l0eV9yZWFsJENBTEMgJWluJSBjKCJGcmVxdWVudGx5IiwgIkFsd2F5cyIpXSA8LSAiRnJlcXVlbnQiCm9iZXNpdHlfcmVhbCRDQUxDXzMgPC0gZmFjdG9yKG9iZXNpdHlfcmVhbCRDQUxDXzMsIGxldmVscyA9IGMoIk5vIiwgIlNvbWV0aW1lcyIsICJGcmVxdWVudCIpKQpwcmludCh0YWJsZShvYmVzaXR5X3JlYWwkQ0FMQ18zKSkKYGBgCgoqKlJhdGlvbmFsZToqKiBUaGUgIkFsd2F5cyIgY2F0ZWdvcnkgY29udGFpbmVkIG5lZ2xpZ2libGUgb2JzZXJ2YXRpb25zIGluIHRoZSByZWFsIGRhdGFzZXQuIFdlIG1lcmdlZCAiQWx3YXlzIiB3aXRoICJGcmVxdWVudGx5IiBpbnRvIGEgc2luZ2xlICJGcmVxdWVudCIgY2F0ZWdvcnkuCgojIyMgQmlubmluZyBDb250aW51b3VzIFZhcmlhYmxlcwoKQ29udGludW91cyB2YXJpYWJsZXMgKEFnZSwgSGVpZ2h0KSBhcmUgYmlubmVkIGludG8gcXVhbnRpbGUtYmFzZWQgY2F0ZWdvcmllcyB0byBhdm9pZCBhc3N1bXB0aW9ucyBvZiBsaW5lYXIgcmVsYXRpb25zaGlwcy4KCmBgYHtyIGJpbm5pbmd9Cm1ha2VfcXVhbnRpbGVfYmlucyA8LSBmdW5jdGlvbih4LCBuX2JpbnMgPSA1LCBsYWJlbHMgPSBOVUxMKSB7CiAgcHJvYnMgPC0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSBuX2JpbnMgKyAxKQogIHFzIDwtIHVuaXF1ZShxdWFudGlsZSh4LCBwcm9icyA9IHByb2JzLCBuYS5ybSA9IFRSVUUpKQogICMgQ3JlYXRlIGJpbnMKICBjdXQoeCwgYnJlYWtzID0gcXMsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSwgbGFiZWxzID0gbGFiZWxzWzE6KGxlbmd0aChxcyktMSldKX0Kb2Jlc2l0eV9yZWFsJEFnZV9iaW41IDwtIG1ha2VfcXVhbnRpbGVfYmlucyggb2Jlc2l0eV9yZWFsJEFnZSxuX2JpbnMgPSA1LCBsYWJlbHMgPSBjKCJWZXJ5X1lvdW5nIiwgIllvdW5nIiwgIkFkdWx0IiwgIk1hdHVyZSIsICJPbGRlciIpKQpvYmVzaXR5X3JlYWwkSGVpZ2h0X2JpbjUgPC0gbWFrZV9xdWFudGlsZV9iaW5zKCBvYmVzaXR5X3JlYWwkSGVpZ2h0LCBuX2JpbnMgPSA1LGxhYmVscyA9IGMoIlZlcnlfU2hvcnQiLCAiU2hvcnQiLCAiTWVkaXVtIiwgIlRhbGwiLCAiVmVyeV9UYWxsIikpCnByaW50KHRhYmxlKG9iZXNpdHlfcmVhbCRBZ2VfYmluNSkpCmJhcnBsb3QodGFibGUob2Jlc2l0eV9yZWFsJEFnZV9iaW41KSwgbWFpbj0iQWdlIEdyb3VwcyIsIGNvbD0iZ3JheSIpCnByaW50KHRhYmxlKG9iZXNpdHlfcmVhbCRIZWlnaHRfYmluNSkpCmJhcnBsb3QodGFibGUob2Jlc2l0eV9yZWFsJEhlaWdodF9iaW41KSwgbWFpbj0iSGVpZ2h0IEdyb3VwcyIsIGNvbD0iZ3JheSIpCmBgYAoKKipOb3RlIG9uIFdlaWdodDoqKiBXZWlnaHQgaXMgaW50ZW50aW9uYWxseSAqKmV4Y2x1ZGVkKiogZnJvbSB0aGUgcHJlZGljdG9yIHNldCBiZWNhdXNlIG9iZXNpdHkgY2xhc3NpZmljYXRpb24gaXMgZGVyaXZlZCBmcm9tIEJNSSAod2hpY2ggdXNlcyB3ZWlnaHQpLiBJbmNsdWRpbmcgd2VpZ2h0IGNyZWF0ZSBjaXJjdWxhciBsb2dpYyBhbmQgaW5mbGF0ZSBtb2RlbCBwZXJmb3JtYW5jZS4KCiMjIyBUYXJnZXQgVmFyaWFibGUgQ3JlYXRpb24KClRoZSBvcmlnaW5hbCA3LWNsYXNzIHRhcmdldCB2YXJpYWJsZSBpcyBncm91cGVkIGludG8gMyBjbGFzc2VzIHRvIGltcHJvdmUgbW9kZWwgc3RhYmlsaXR5IGdpdmVuIG91ciByZWR1Y2VkIHNhbXBsZSBzaXplLgoKYGBge3IgY3JlYXRlLXRhcmdldH0Kb2Jlc2l0eV9yZWFsJE9iZXNpdHlfMyA8LSBOQV9jaGFyYWN0ZXJfCm9iZXNpdHlfcmVhbCRPYmVzaXR5XzNbb2Jlc2l0eV9yZWFsJE5PYmV5ZXNkYWQgJWluJSBjKCJJbnN1ZmZpY2llbnRfV2VpZ2h0IiwgIk5vcm1hbF9XZWlnaHQiKV0gPC0gIk5vcm1hbF9vcl9VbmRlciIKb2Jlc2l0eV9yZWFsJE9iZXNpdHlfM1tvYmVzaXR5X3JlYWwkTk9iZXllc2RhZCAlaW4lIGMoIk92ZXJ3ZWlnaHRfTGV2ZWxfSSIsICJPdmVyd2VpZ2h0X0xldmVsX0lJIildIDwtICJPdmVyd2VpZ2h0IgpvYmVzaXR5X3JlYWwkT2Jlc2l0eV8zW29iZXNpdHlfcmVhbCROT2JleWVzZGFkICVpbiUgYygiT2Jlc2l0eV9UeXBlX0kiLCAiT2Jlc2l0eV9UeXBlX0lJIiwgIk9iZXNpdHlfVHlwZV9JSUkiKV0gPC0gIk9iZXNlIgpvYmVzaXR5X3JlYWwkT2Jlc2l0eV8zIDwtIGZhY3RvcihvYmVzaXR5X3JlYWwkT2Jlc2l0eV8zLCBsZXZlbHMgPSBjKCJOb3JtYWxfb3JfVW5kZXIiLCAiT3ZlcndlaWdodCIsICJPYmVzZSIpKQpjYXQoIi0tLSBUYXJnZXQgVmFyaWFibGUgRGlzdHJpYnV0aW9uICgzIENsYXNzZXMpIC0tLVxuIikKcHJpbnQodGFibGUob2Jlc2l0eV9yZWFsJE9iZXNpdHlfMykpCnByaW50KHByb3AudGFibGUodGFibGUob2Jlc2l0eV9yZWFsJE9iZXNpdHlfMykpICogMTAwKSAjIFNob3cgcGVyY2VudGFnZXMKYmFycGxvdCh0YWJsZShvYmVzaXR5X3JlYWwkT2Jlc2l0eV8zKSwgCiAgICAgICAgbWFpbiA9ICJUYXJnZXQgRGlzdHJpYnV0aW9uICgzIENsYXNzZXMpIiwgCiAgICAgICAgY29sID0gYygiZ3JlZW4iLCAib3JhbmdlIiwgInJlZCIpLAogICAgICAgIHlsYWIgPSAiQ291bnQiKQpgYGAKCioqUmF0aW9uYWxlOioqIFdpdGggb25seSA1MzEgcmVhbCBvYnNlcnZhdGlvbnMsIHRoZSA3LWNsYXNzIHByb2JsZW0gd291bGQgaGF2ZSBpbnN1ZmZpY2llbnQgc2FtcGxlcyBwZXIgY2xhc3MgZm9yIHJlbGlhYmxlIG1vZGVsaW5nLiBUaGUgMy1jbGFzcyBncm91cGluZyBlbnN1cmUgYWRlcXVhdGUgY2xhc3Mgc2l6ZXMuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFRyYWluL1Rlc3QgU3BsaXQKCmBgYHtyIHRyYWluLXRlc3Qtc3BsaXR9CnNldC5zZWVkKDEyMykKbiA8LSBucm93KG9iZXNpdHlfcmVhbCkKdHJhaW5fc2l6ZSA8LSBmbG9vcigwLjggKiBuKQp0cmFpbl9pbmRleCA8LSBzYW1wbGUoMTpuLCBzaXplID0gdHJhaW5fc2l6ZSwgcmVwbGFjZSA9IEZBTFNFKQpvYl90cmFpbiA8LSBvYmVzaXR5X3JlYWxbdHJhaW5faW5kZXgsIF0Kb2JfdGVzdCAgPC0gb2Jlc2l0eV9yZWFsWy10cmFpbl9pbmRleCwgXQpgYGAKCiMjIyBDbGFzcyBEaXN0cmlidXRpb24gVmVyaWZpY2F0aW9uCgp8IENsYXNzICAgICAgICAgICB8IFRyYWluaW5nIFNldCAobj00MjQpIHwgVGVzdCBTZXQgKG49MTA3KSB8Cnw6LS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLXwKfCBOb3JtYWxfb3JfVW5kZXIgfCA2MiUgICAgICAgICAgICAgICAgICB8IDU5LjglICAgICAgICAgICAgfAp8IE92ZXJ3ZWlnaHQgICAgICB8IDI1JSAgICAgICAgICAgICAgICAgIHwgMjQuMyUgICAgICAgICAgICB8CnwgT2Jlc2UgICAgICAgICAgIHwgMTMlICAgICAgICAgICAgICAgICAgfCAxNS45JSAgICAgICAgICAgIHwKClRoZSBzaW1pbGFyIGNsYXNzIGRpc3RyaWJ1dGlvbnMgY29uZmlybSB0aGF0IG91ciByYW5kb20gc3BsaXQgcHJvZHVjZWQgcmVwcmVzZW50YXRpdmUgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRmluYWwgUHJlZGljdG9yIFNldAoKQmFzZWQgb24gdGhlIHRyYW5zZm9ybWF0aW9ucyBhYm92ZSwgb3VyIGZpbmFsIHByZWRpY3RvciBzZXQgY29uc2lzdHMgb2YgMTUgdmFyaWFibGVzOgoKYGBge3IgZGVmaW5lLXByZWRpY3RvcnN9CmFsbG93ZWRfcHJlZGljdG9ycyA8LSBjKCJHZW5kZXIiLCJBZ2VfYmluNSIsIkhlaWdodF9iaW41IiwiZmFtaWx5X2hpc3Rvcnlfd2l0aF9vdmVyd2VpZ2h0IiwiRkFWQyIsCiAgIkZDVkMiLCJDQUVDIiwiU01PS0UiLCJDSDJPIiwiU0NDIiwiRkFGIiwiVFVFIiwiTkNQXzMiLCJDQUxDXzMiLCJNVFJBTlNfMyIpCmBgYAoKKipFeGNsdWRlZCB2YXJpYWJsZXM6KioKCi0gICAqKldlaWdodDoqKiBDaXJjdWxhciByZWxhdGlvbnNoaXAgd2l0aCB0YXJnZXQgKEJNSSB1c2VzIHdlaWdodCkKLSAgICoqSGVpZ2h0IChyYXcpOioqIFJlcGxhY2VkIHdpdGggSGVpZ2h0X2JpbjUKLSAgICoqQWdlIChyYXcpOioqIFJlcGxhY2VkIHdpdGggQWdlX2JpbjUKLSAgICoqT3JpZ2luYWwgTVRSQU5TLCBOQ1AsIENBTEM6KiogUmVwbGFjZWQgd2l0aCBtZXJnZWQgdmVyc2lvbnMKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzCgojIyBUYXJnZXQgVmFyaWFibGUgRGlzdHJpYnV0aW9uCgpgYGB7ciBlZGEtdGFyZ2V0fQp0YXJnZXRfY291bnRzIDwtIHRhYmxlKG9iZXNpdHlfcmVhbCRPYmVzaXR5XzMpCnRhcmdldF9wY3QgPC0gcm91bmQocHJvcC50YWJsZSh0YXJnZXRfY291bnRzKSAqIDEwMCwgMSkKYnAgPC0gYmFycGxvdCh0YXJnZXRfY291bnRzLCBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBPYmVzaXR5IExldmVscyAoMyBDbGFzc2VzKSIsIGNvbCA9IGMoImZvcmVzdGdyZWVuIiwgIm9yYW5nZSIsICJmaXJlYnJpY2siKSwgeWxpbSA9IGMoMCwgbWF4KHRhcmdldF9jb3VudHMpICogMS4yKSx5bGFiID0gIkNvdW50IikKdGV4dCh4ID0gYnAsIHkgPSB0YXJnZXRfY291bnRzLCBsYWJlbHMgPSBwYXN0ZTAodGFyZ2V0X2NvdW50cywgIlxuKCIsIHRhcmdldF9wY3QsICIlKSIpLCBwb3MgPSAzLCBjZXggPSAwLjkpCmBgYAoKIyMjIEludGVycHJldGF0aW9uCgpUaGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB0YXJnZXQgdmFyaWFibGUgaXMgY2xlYXJseSBpbWJhbGFuY2VkLiBUaGlzIGNsYXNzIGltYmFsYW5jZSBpbXBsaWVzIHRoYXQgTW9kZWxzIG1heSBuYXR1cmFsbHkgZmF2b3IgdGhlIG1ham9yaXR5IGNsYXNzLiBtZXRob2RzIHJvYnVzdCB0byB1bmV2ZW4gY2xhc3Mgc2l6ZXMgc3VjaCBhcyBSYW5kb20gRm9yZXN0IG1heSB3b3JrIGJldHRlciB3aGVuIHByZWRpY3Rpbmcgb2Jlc2l0eSBsZXZlbHMuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFByZWRpY3RvciBEaXN0cmlidXRpb25zCgojIyMgRGVtb2dyYXBoaWMgVmFyaWFibGVzCgpgYGB7ciBlZGEtZGVtb2dyYXBoaWN9CnBhcihtZnJvdyA9IGMoMSwgMykpCmdlbmRlcl9jb3VudHMgPC0gdGFibGUob2Jlc2l0eV9yZWFsJEdlbmRlcikKYnBfZyA8LSBiYXJwbG90KGdlbmRlcl9jb3VudHMsIG1haW4gPSAiR2VuZGVyIERpc3RyaWJ1dGlvbiIsIGNvbCA9IGMoImxpZ2h0cGluayIsICJsaWdodGJsdWUiKSx5bGltID0gYygwLCBtYXgoZ2VuZGVyX2NvdW50cykgKiAxLjE1KSkKdGV4dChicF9nLCBnZW5kZXJfY291bnRzLCBsYWJlbHMgPSBnZW5kZXJfY291bnRzLCBwb3MgPSAzKQphZ2VfY291bnRzIDwtIHRhYmxlKG9iZXNpdHlfcmVhbCRBZ2VfYmluNSkKYnBfYSA8LSBiYXJwbG90KGFnZV9jb3VudHMsIG1haW4gPSAiQWdlIEdyb3VwcyAoUXVhbnRpbGVzKSIsIGNvbCA9ICJncmF5ODAiLGxhcyA9IDIsY2V4Lm5hbWVzID0gMC44KQpoZWlnaHRfY291bnRzIDwtIHRhYmxlKG9iZXNpdHlfcmVhbCRIZWlnaHRfYmluNSkKYnBfaCA8LSBiYXJwbG90KGhlaWdodF9jb3VudHMsIG1haW4gPSAiSGVpZ2h0IEdyb3VwcyAoUXVhbnRpbGVzKSIsIGNvbCA9ICJncmF5ODAiLGxhcyA9IDIsY2V4Lm5hbWVzID0gMC44KQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCioqS2V5IG9ic2VydmF0aW9uczoqKgoKLSAgIFRoZXJlIGlzIG5vIHNldmVyZSBnZW5kZXIgaW1iYWxhbmNlIHRoYXQgd291bGQgYmlhcyB0aGUgbW9kZWwgdG93YXJkIG9uZSBncm91cC4gSGVpZ2h0IGFuZCBBZ2UgaXMgdW5saWtlbHkgdG8gaW50cm9kdWNlIGJpYXMuIEl0IG1heSBwbGF5IGEgcm9sZSBpbmRpcmVjdGx5LCBhcyBoZWlnaHQgaW50ZXJhY3RzIHdpdGggQk1JLXJlbGF0ZWQgcGh5c2lvbG9neS4KCiMjIyBFYXRpbmcgSGFiaXQgVmFyaWFibGVzCgpgYGB7ciBlZGEtZWF0aW5nfQpwYXIobWZyb3cgPSBjKDIsIDMpKQpiYXJwbG90KHRhYmxlKG9iZXNpdHlfcmVhbCRGQVZDKSxtYWluID0gIkhpZ2ggQ2Fsb3JpZSBGb29kIChGQVZDKSIsIGNvbCA9ICJsaWdodGJsdWUiLCBsYXMgPSAxKQpiYXJwbG90KHRhYmxlKG9iZXNpdHlfcmVhbCRGQ1ZDKSwgbWFpbiA9ICJWZWdldGFibGUgQ29ucy4gKEZDVkMpIiwgeGxhYiA9ICJMZXZlbCAoMT1OZXZlciwgMz1BbHdheXMpIixjb2wgPSAibGlnaHRibHVlIiwgbGFzID0gMSkKYmFycGxvdCh0YWJsZShvYmVzaXR5X3JlYWwkTkNQXzMpLG1haW4gPSAiTnVtYmVyIG9mIE1lYWxzIChOQ1ApIixjb2wgPSAibGlnaHRibHVlIiwgbGFzID0gMSkKYmFycGxvdCh0YWJsZShvYmVzaXR5X3JlYWwkQ0FFQyksIG1haW4gPSAiU25hY2tpbmcgSGFiaXRzIChDQUVDKSIsY29sID0gImxpZ2h0Ymx1ZSIsIGxhcyA9IDEsIGNleC5uYW1lcyA9IDAuOCkKYmFycGxvdCh0YWJsZShvYmVzaXR5X3JlYWwkQ0gyTyksIG1haW4gPSAiRGFpbHkgV2F0ZXIgSW50YWtlIChDSDJPKSIseGxhYiA9ICJMZXZlbCAoMT1Mb3csIDM9SGlnaCkiLGNvbCA9ICJsaWdodGJsdWUiLCBsYXMgPSAxKQpiYXJwbG90KHRhYmxlKG9iZXNpdHlfcmVhbCRDQUxDXzMpLCBtYWluID0gIkFsY29ob2wgQ29ucy4gKENBTEMpIiwgY29sID0gImxpZ2h0Ymx1ZSIsIGxhcyA9IDEpCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKKipLZXkgb2JzZXJ2YXRpb25zOioqCgotICAgQXBwcm94aW1hdGVseSA5MCUgb2YgcGFydGljaXBhbnRzIGFuc3dlcmVkICJZZXMiIHRvIEZBVkMuIFNpbmNlIGFsbW9zdCBldmVyeW9uZSBjb25zdW1lcyBoaWdoLWNhbG9yaWUgZm9vZCwgdGhpcyB2YXJpYWJsZSBoYXMgbG93IHZhcmlhbmNlIGFuZCBtYXkgbm90IGVmZmVjdGl2ZWx5IGRpc3Rpbmd1aXNoIGJldHdlZW4gT2Jlc2UgYW5kIE5vcm1hbCBpbmRpdmlkdWFscyBpbiB0aGUgZGVjaXNpb24gdHJlZS4gaW4gb3RoZXIgaGFuZCB0aGUgRkNWQyBkYXRhIHNob3dzIGEgZ29vZCBzcHJlYWQgYWNyb3NzIGFsbCB0aHJlZSBsZXZlbHMuIFRoZSBiYWxhbmNlZCBkaXN0cmlidXRpb24gc3VnZ2VzdHMgdGhpcyB3aWxsIGJlIGEgc3Ryb25nIHByZWRpY3RvciBmb3IgZGlzdGluZ3Vpc2hpbmcgaGVhbHRoLWNvbnNjaW91cyBpbmRpdmlkdWFscy4KCiMjIyBMaWZlc3R5bGUgVmFyaWFibGVzCgpgYGB7ciBlZGEtbGlmZXN0eWxlfQpwYXIobWZyb3cgPSBjKDIsIDMpKQoKYmFycGxvdCh0YWJsZShvYmVzaXR5X3JlYWwkU01PS0UpLG1haW4gPSAiU21va2luZyBTdGF0dXMiLCBjb2wgPSAibGlnaHRzYWxtb24iLCBsYXMgPSAxKQpiYXJwbG90KHRhYmxlKG9iZXNpdHlfcmVhbCRTQ0MpLG1haW4gPSAiQ2Fsb3JpZSBNb25pdG9yaW5nIChTQ0MpIiwgY29sID0gImxpZ2h0c2FsbW9uIiwgbGFzID0gMSkKYmFycGxvdCh0YWJsZShvYmVzaXR5X3JlYWwkRkFGKSxtYWluID0gIlBoeXNpY2FsIEFjdGl2aXR5IChGQUYpIiwgeGxhYiA9ICJMZXZlbCAoMD1Ob25lLCAzPUhpZ2gpIixjb2wgPSAibGlnaHRzYWxtb24iLCBsYXMgPSAxKQpiYXJwbG90KHRhYmxlKG9iZXNpdHlfcmVhbCRUVUUpLCBtYWluID0gIlRlY2ggVXNlIFRpbWUgKFRVRSkiLCB4bGFiID0gIkxldmVsICgwPUxvdywgMj1IaWdoKSIsY29sID0gImxpZ2h0c2FsbW9uIiwgbGFzID0gMSkKYmFycGxvdCh0YWJsZShvYmVzaXR5X3JlYWwkTVRSQU5TXzMpLCBtYWluID0gIlRyYW5zcG9ydCBNb2RlIChNVFJBTlMpIiwgY29sID0gImxpZ2h0Z3JlZW4iLCBsYXMgPSAyLCBjZXgubmFtZXMgPSAwLjgpCmJhcnBsb3QodGFibGUob2Jlc2l0eV9yZWFsJGZhbWlseV9oaXN0b3J5X3dpdGhfb3ZlcndlaWdodCksIG1haW4gPSAiRmFtaWx5IEhpc3RvcnkgKE92ZXJ3ZWlnaHQpIiwgY29sID0gImxpZ2h0c2FsbW9uIiwgbGFzID0gMSkKcGFyKG1mcm93ID0gYygxLCAxKSkKYGBgCgoqKktleSBvYnNlcnZhdGlvbnM6KioKClRoZSBtb3N0IGluZmx1ZW50aWFsIGFtb25nIHRoZSBsaWZlc3R5bGUgdmFyaWFibGVzIGlzIGV4cGVjdGVkIHRvIGJlIHRoZSBmYW1pbHkgaGlzdG9yeSBvZiBvdmVyd2VpZ2h0LiBUaGlzIHZhcmlhYmxlIHNob3dzIGEgc2hhcnAgYW5kIG1lYW5pbmdmdWwgY29udHJhc3QgYmV0d2VlbiBncm91cHMgYW5kIGNhcHR1cmVzIGEgY29tYmluZWQgZWZmZWN0IG9mIGdlbmV0aWMgcHJlZGlzcG9zaXRpb24gYW5kIGxvbmctdGVybSBzaGFyZWQgZW52aXJvbm1lbnQuT3VyIG1vZGVsIHJlc3VsdHMgaW5kZWVkIGNvbmZpcm0gdGhhdCB0aGlzIHZhcmlhYmxlIGJlY29tZXMgb25lIG9mIHRoZSBzdHJvbmdlc3QgcHJlZGljdG9ycyBhbmQgb2Z0ZW4gYXBwZWFycyBhbW9uZyB0aGUgZmlyc3Qgc3BsaXRzIGluIGRlY2lzaW9uIHRyZWVzIGFuZCB0b3AtcmFua2VkIGZlYXR1cmVzIGluIFJhbmRvbSBGb3Jlc3QgaW1wb3J0YW5jZSBtZWFzdXJlcy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVsYXRpb25zaGlwcyB3aXRoIFRhcmdldAoKIyMjIEtleSBQcmVkaWN0b3ItVGFyZ2V0IEFzc29jaWF0aW9ucwoKYGBge3IgZWRhLXJlbGF0aW9uc2hpcHN9CgpwbG90X2JpdmFyaWF0ZV9iYXNlIDwtIGZ1bmN0aW9uKGRhdGEsIHhfdmFyLCBmaWxsX3ZhciwgdGl0bGUsIGNvbF9wYWxldHRlID0gTlVMTCkge3RhYiA8LSB0YWJsZShkYXRhW1t4X3Zhcl1dLCBkYXRhW1tmaWxsX3Zhcl1dKQpwcm9wX3RhYiA8LSBwcm9wLnRhYmxlKHRhYiwgbWFyZ2luID0gMSkKICBpZiAoaXMubnVsbChjb2xfcGFsZXR0ZSkpIHtjb2xfcGFsZXR0ZSA8LSByYWluYm93KG5jb2wocHJvcF90YWIpKX0KICBiYXJwbG90KHQocHJvcF90YWIpLCBiZXNpZGUgPSBGQUxTRSxjb2wgPSBjb2xfcGFsZXR0ZSxtYWluID0gdGl0bGUseGxhYiA9ICJPYmVzaXR5IENsYXNzIiwgeWxhYiA9ICJQZXJjZW50YWdlIHdpdGhpbiBDbGFzcyIseWxpbSA9IGMoMCwgMSksbGFzID0gMSkgCiAgbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IHJldihjb2xuYW1lcyhwcm9wX3RhYikpLCAgZmlsbCA9IHJldihjb2xfcGFsZXR0ZSksdGl0bGUgPSBmaWxsX3ZhcixjZXggPSAwLjYsYnR5ID0gIm8iLGJnID0gIndoaXRlIikgICB9CnBhcihtZnJvdyA9IGMoMiwgMiksIG1hciA9IGMoNSwgNCwgNCwgMikpICAgCmNvbF9wYXN0ZWwxIDwtIGMoIiNGQkI0QUUiLCAiI0IzQ0RFMyIpICAKcGxvdF9iaXZhcmlhdGVfYmFzZShvYmVzaXR5X3JlYWwsICJPYmVzaXR5XzMiLCAiZmFtaWx5X2hpc3Rvcnlfd2l0aF9vdmVyd2VpZ2h0IiwgIkZhbWlseSBIaXN0b3J5IGJ5IE9iZXNpdHkgQ2xhc3MiLCBjb2xfcGFzdGVsMSkKY29sX2JsdWVzIDwtIGMoIiNFRkYzRkYiLCAiI0JERDdFNyIsICIjNkJBRUQ2IiwgIiMyMTcxQjUiKQpwbG90X2JpdmFyaWF0ZV9iYXNlKG9iZXNpdHlfcmVhbCwgIk9iZXNpdHlfMyIsICJGQUYiLCAiUGh5c2ljYWwgQWN0aXZpdHkgKEZBRikgYnkgT2Jlc2l0eSBDbGFzcyIsIGNvbF9ibHVlcykKY29sX2dyZXlzIDwtIGMoIiNGN0Y3RjciLCAiI0NDQ0NDQyIsICIjOTY5Njk2IiwgIiM1MjUyNTIiKQpwbG90X2JpdmFyaWF0ZV9iYXNlKG9iZXNpdHlfcmVhbCwgIk9iZXNpdHlfMyIsICJBZ2VfYmluNSIsICJBZ2UgRGlzdHJpYnV0aW9uIGJ5IE9iZXNpdHkgQ2xhc3MiLCBjb2xfZ3JleXMpCmNvbF9vcmFuZ2VzIDwtIGMoIiNGRUU2Q0UiLCAiI0U2NTUwRCIpCnBsb3RfYml2YXJpYXRlX2Jhc2Uob2Jlc2l0eV9yZWFsLCAiT2Jlc2l0eV8zIiwgIkZBVkMiLCAiSGlnaCBDYWxvcmllIEZvb2QgQ29uc3VtcHRpb24gYnkgT2Jlc2l0eSBDbGFzcyIsIGNvbF9vcmFuZ2VzKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCiMjIyBJbnRlcnByZXRhdGlvbgoKVGhlIHBsb3RzIHNob3cgdGhhdCBoaWdoLWNhbG9yaWUgZm9vZCBjb25zdW1wdGlvbiBpcyB1YmlxdWl0b3VzLCB3aXRoIG5lYXJseSA5MCUgb2YgdGhlIGVudGlyZSBwb3B1bGF0aW9uIChyZWdhcmRsZXNzIG9mIHdlaWdodCBjbGFzcykgcmVwb3J0aW5nICJZZXMuIiBXaGlsZSB0aGUgT2Jlc2UgZ3JvdXAgZG9lcyBzaG93IGEgc2xpZ2h0bHkgaGlnaGVyIHNhdHVyYXRpb24gb2YgIlllcyIgcmVzcG9uc2VzIGNvbXBhcmVkIHRvIHRoZSBOb3JtYWwgZ3JvdXAsIHRoZSBsYWNrIG9mIGEgc2hhcnAgY29udHJhc3Qgc3VnZ2VzdHMgdGhhdCB3aGF0IHBlb3BsZSBlYXQgKGhpZ2ggY2Fsb3JpZSkgaXMgbGVzcyBwcmVkaWN0aXZlIHRoYW4gaG93IG11Y2ggdGhleSBlYXQgKE5DUCkgb3IgdGhlaXIgZ2VuZXRpYyBiYWNrZ3JvdW5kIChGYW1pbHkgSGlzdG9yeSkKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBNZXRob2RvbG9neQoKIyMgRGVjaXNpb24gVHJlZXMKCkRlY2lzaW9uIHRyZWVzIHBhcnRpdGlvbiB0aGUgcHJlZGljdG9yIHNwYWNlIGludG8gcmVnaW9ucyB0aHJvdWdoIHJlY3Vyc2l2ZSBiaW5hcnkgc3BsaXR0aW5nLiBBdCBlYWNoIG5vZGUsIHRoZSBhbGdvcml0aG0gc2VsZWN0cyB0aGUgdmFyaWFibGUgYW5kIHNwbGl0IHBvaW50IHRoYXQgYmVzdCBzZXBhcmF0ZXMgdGhlIGNsYXNzZXMgYWNjb3JkaW5nIHRvIGFuIGltcHVyaXR5IG1lYXN1cmUuCgoqKlNwbGl0dGluZyBDcml0ZXJpYToqKgoKLSAgICoqRGV2aWFuY2UgKEVudHJvcHkpOioqIE1lYXN1cmVzIGluZm9ybWF0aW9uIGdhaW47IHByZWZlcnMgc3BsaXRzIHRoYXQgY3JlYXRlIHB1cmVyIG5vZGVzCi0gICAqKkdpbmkgSW5kZXg6KiogTWVhc3VyZXMgY2xhc3MgcHJvYmFiaWxpdHkgZGlzcGVyc2lvbjsgY29tbW9ubHkgdXNlZCBpbiBDQVJUCgoqKkNyb3NzLVZhbGlkYXRpb24gZm9yIFRyZWUgU2l6ZToqKiBMYXJnZSB0cmVlcyB0ZW5kIHRvIG92ZXJmaXQgdGhlIHRyYWluaW5nIGRhdGEuIFdlIHVzZSBLLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiB0byBpZGVudGlmeSB0aGUgb3B0aW1hbCB0cmVlIHNpemUgdGhhdCBtaW5pbWl6ZXMgcHJlZGljdGlvbiBlcnJvciBvbiBoZWxkLW91dCBkYXRhLgoKKipQcnVuaW5nOioqIEFmdGVyIGRldGVybWluaW5nIHRoZSBvcHRpbWFsIHNpemUgdmlhIGNyb3NzLXZhbGlkYXRpb24sIHdlIHBydW5lIHRoZSB0cmVlIGJ5IHJlbW92aW5nIHNwbGl0cyB0aGF0IGRvIG5vdCBpbXByb3ZlIGNyb3NzLXZhbGlkYXRlZCBwZXJmb3JtYW5jZS4gVGhpcyBwcm9kdWNlcyBhIHNpbXBsZXIsIG1vcmUgZ2VuZXJhbGl6YWJsZSBtb2RlbC4KCioqUiBJbXBsZW1lbnRhdGlvbjoqKiBXZSB1c2UgdGhlIGB0cmVlYCBwYWNrYWdlIHdpdGggYGN2LnRyZWUoKWAgZm9yIGNyb3NzLXZhbGlkYXRpb24gYW5kIGBwcnVuZS5taXNjbGFzcygpYCBmb3IgcHJ1bmluZyBiYXNlZCBvbiBtaXNjbGFzc2lmaWNhdGlvbiByYXRlLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBCYWdnaW5nIChCb290c3RyYXAgQWdncmVnYXRpbmcpCgpCYWdnaW5nIHJlZHVjZXMgdmFyaWFuY2UgYnkgdHJhaW5pbmcgbXVsdGlwbGUgdHJlZXMgb24gYm9vdHN0cmFwIHNhbXBsZXMgb2YgdGhlIHRyYWluaW5nIGRhdGEgYW5kIGF2ZXJhZ2luZyB0aGVpciBwcmVkaWN0aW9ucy4KCioqS2V5IGNoYXJhY3RlcmlzdGljczoqKgoKLSAgIEVhY2ggdHJlZSBpcyB0cmFpbmVkIG9uIGEgYm9vdHN0cmFwIHNhbXBsZSAoc2FtcGxpbmcgd2l0aCByZXBsYWNlbWVudCkKLSAgIEF0IGVhY2ggc3BsaXQsICoqYWxsIHAgcHJlZGljdG9ycyoqIGFyZSBjb25zaWRlcmVkIChtdHJ5ID0gcCkKLSAgIEZpbmFsIHByZWRpY3Rpb24gaXMgYnkgbWFqb3JpdHkgdm90ZSBhY3Jvc3MgYWxsIHRyZWVzCi0gICBPdXQtb2YtYmFnIChPT0IpIG9ic2VydmF0aW9ucyBwcm92aWRlIGJ1aWx0LWluIHZhbGlkYXRpb24KCioqUiBJbXBsZW1lbnRhdGlvbjoqKiBXZSB1c2UgYHJhbmRvbUZvcmVzdCgpYCB3aXRoIGBtdHJ5ID0gcGAgKG51bWJlciBvZiBwcmVkaWN0b3JzID0gMTUpIGFuZCBgbnRyZWUgPSA1MDBgLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBSYW5kb20gRm9yZXN0CgpSYW5kb20gRm9yZXN0IGV4dGVuZHMgYmFnZ2luZyBieSBpbnRyb2R1Y2luZyBhZGRpdGlvbmFsIHJhbmRvbW5lc3M6IGF0IGVhY2ggc3BsaXQsIG9ubHkgYSByYW5kb20gc3Vic2V0IG9mIHByZWRpY3RvcnMgaXMgY29uc2lkZXJlZC4KCioqS2V5IGNoYXJhY3RlcmlzdGljczoqKgoKLSAgIFNhbWUgYm9vdHN0cmFwIHNhbXBsaW5nIGFzIGJhZ2dpbmcKLSAgIEF0IGVhY2ggc3BsaXQsIG9ubHkgKiriiJpwIHByZWRpY3RvcnMqKiBhcmUgcmFuZG9tbHkgc2VsZWN0ZWQgKG10cnkg4omIIDQgZm9yIG91ciAxNSBwcmVkaWN0b3JzKQotICAgVGhpcyBkZWNvcnJlbGF0ZXMgdGhlIHRyZWVzLCBmdXJ0aGVyIHJlZHVjaW5nIHZhcmlhbmNlCi0gICBWYXJpYWJsZSBpbXBvcnRhbmNlIG1lYXN1cmVzIGluZGljYXRlIHByZWRpY3RvciBjb250cmlidXRpb25zCgoqKlIgSW1wbGVtZW50YXRpb246KiogV2UgdXNlIGByYW5kb21Gb3Jlc3QoKWAgd2l0aCBkZWZhdWx0IG10cnkgKOKImnAgZm9yIGNsYXNzaWZpY2F0aW9uKSBhbmQgYG50cmVlID0gNTAwYC4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBSZXN1bHRzCgojIyBEZWNpc2lvbiBUcmVlIFJlc3VsdHMKCiMjIyBNb2RlbCAxOiBEZXZpYW5jZS1CYXNlZCBUcmVlCgpgYGB7ciB0cmVlLWRldmlhbmNlLWZpdH0KdHJhaW5fZGF0YSA8LSBvYl90cmFpblssIGMoIk9iZXNpdHlfMyIsIGFsbG93ZWRfcHJlZGljdG9ycyldCnRlc3RfZGF0YSAgPC0gb2JfdGVzdFssIGMoIk9iZXNpdHlfMyIsIGFsbG93ZWRfcHJlZGljdG9ycyldCnRyZWVfZGV2aWFuY2UgPC0gdHJlZShPYmVzaXR5XzMgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSwgc3BsaXQgPSAiZGV2aWFuY2UiKQpzdW1tYXJ5KHRyZWVfZGV2aWFuY2UpCnBsb3QodHJlZV9kZXZpYW5jZSk7IHRleHQodHJlZV9kZXZpYW5jZSwgcHJldHR5PTApCmBgYAoKT3VyIERldmlhbmNlIHRyZWUgbW9kZWwgcHJlZGljdHMgb2Jlc2l0eSBjbGFzc2lmaWNhdGlvbiB1c2luZyAxOCBkaWZmZXJlbnQgZ3JvdXBzIGJhc2VkIG9uIDEwIHZhcmlhYmxlcy4gVGhlIG1vZGVsIGNvcnJlY3RseSBjbGFzc2lmaWVzIGFib3V0IDczLjYlIG9mIGNhc2VzLCB3aGljaCBtZWFucyBpdCBtYWtlcyBtaXN0YWtlcyBhYm91dCAyNi40JSBvZiB0aGUgdGltZS4gQWdlIGlzIHRoZSBmaXJzdCBzcGxpdCwgYW5kIHRoZW4gdGhlIG1vZGVsIHVzZXMgZWF0aW5nIGZyZXF1ZW5jeSwgaGVpZ2h0LCBhbmQgd2F0ZXIgY29uc3VtcHRpb24gdG8gbWFrZSBmdXJ0aGVyIGRlY2lzaW9ucy4gVGhlIGVycm9yIHJhdGUgc2hvd3MgdGhhdCBvYmVzaXR5IGlzIGhhcmQgdG8gcHJlZGljdCB3aXRoIGp1c3QgdGhlc2UgZmVhdHVyZXMgYmVjYXVzZSBkaWZmZXJlbnQgb2Jlc2l0eSBncm91cHMgaGF2ZSBzaW1pbGFyIGNoYXJhY3RlcmlzdGljcy4KCiMjIyMgQ3Jvc3MtVmFsaWRhdGlvbiBmb3IgT3B0aW1hbCBTaXplCgpgYGB7ciB0cmVlLWRldmlhbmNlLWN2fQpzZXQuc2VlZCgyMDI0KQpjdl9kZXZpYW5jZSA8LSBjdi50cmVlKHRyZWVfZGV2aWFuY2UsIEZVTiA9IHBydW5lLm1pc2NsYXNzKQpwcmludChjdl9kZXZpYW5jZSkKcGxvdChjdl9kZXZpYW5jZSRzaXplLCBjdl9kZXZpYW5jZSRkZXYsIHR5cGUgPSAiYiIsIHBjaCA9IDE5LCBjb2wgPSAiZGFya3JlZCIsIGx3ZCA9IDIseGxhYiA9ICJUcmVlIFNpemUgKE51bWJlciBvZiBUZXJtaW5hbCBOb2RlcykiLCB5bGFiID0gIkNWIE1pc2NsYXNzaWZpY2F0aW9uIEVycm9yIixtYWluID0gIkNyb3NzLVZhbGlkYXRpb246IEVycm9yIHZzIENvbXBsZXhpdHkiKQpncmlkKCkKYmVzdF9zaXplX2RldiA8LSBjdl9kZXZpYW5jZSRzaXplW3doaWNoLm1pbihjdl9kZXZpYW5jZSRkZXYpXQpjYXQoIlxuT3B0aW1hbCBUcmVlIFNpemUgKE5vZGVzKToiLCBiZXN0X3NpemVfZGV2LCAiXG4iKQpgYGAKCkNyb3NzLXZhbGlkYXRpb24gd2FzIHVzZWQgdG8gZmluZCB0aGUgYmVzdCB0cmVlIHNpemUgdGhhdCBiYWxhbmNlcyBhY2N1cmFjeSBhbmQgc2ltcGxpY2l0eS4gVGhlIHJlc3VsdHMgc2hvdyB0aGF0IGEgdHJlZSB3aXRoIDEwIG5vZGVzIGhhcyB0aGUgbG93ZXN0IGNyb3NzLXZhbGlkYXRpb24gZXJyb3Igb2YgMTUxIG1pc2NsYXNzaWZpY2F0aW9ucy4gVGhpcyBpcyBiZXR0ZXIgdGhhbiB0aGUgb3JpZ2luYWwgdHJlZSB3aXRoIDE4IG5vZGVzLCB3aGljaCBoYWQgMTU4IGVycm9ycy4gQSBzbWFsbGVyIHRyZWUgaXMgZWFzaWVyIHRvIHVuZGVyc3RhbmQgYW5kIHdvcmtzIGJldHRlciBvbiBuZXcgZGF0YSBiZWNhdXNlIGl0IGF2b2lkcyBvdmVyZml0dGluZy4gVGhlIG9wdGltYWwgdHJlZSBzaXplIG9mIDEwIG5vZGVzIHJlbW92ZXMgdW5uZWNlc3Nhcnkgc3BsaXRzIHdoaWxlIGtlZXBpbmcgZ29vZCBwcmVkaWN0aW9uIHBlcmZvcm1hbmNlLgoKIyMjIyBQcnVuZWQgVHJlZSBWaXN1YWxpemF0aW9uCgpgYGB7ciB0cmVlLWRldmlhbmNlLXBydW5lZH0KdHJlZV9kZXZpYW5jZV9wcnVuZWQgPC0gcHJ1bmUubWlzY2xhc3ModHJlZV9kZXZpYW5jZSwgYmVzdCA9IGJlc3Rfc2l6ZV9kZXYpCnBsb3QodHJlZV9kZXZpYW5jZV9wcnVuZWQpCnRleHQodHJlZV9kZXZpYW5jZV9wcnVuZWQsIHByZXR0eSA9IDAsIGNleCA9IDAuOCwgY29sID0gImJsdWUiKQp0aXRsZShwYXN0ZSgiUHJ1bmVkIERldmlhbmNlIFRyZWUgKFNpemUgPSIsIGJlc3Rfc2l6ZV9kZXYsICIpIikpCmBgYAoKVGhlIHBydW5lZCB0cmVlIHdpdGggMTAgbm9kZXMgaXMgc2ltcGxlciBhbmQgZWFzaWVyIHRvIGludGVycHJldCB0aGFuIHRoZSBvcmlnaW5hbCB0cmVlLiBUaGUgbWFpbiBzcGxpdHMgYXJlIHN0aWxsIGFnZSwgZWF0aW5nIGZyZXF1ZW5jeSAoQ0FFQyksIGFuZCBoZWlnaHQsIHdoaWNoIGFyZSB0aGUgbW9zdCBpbXBvcnRhbnQgZmFjdG9ycyBmb3Igb2Jlc2l0eS4gRm9yIHlvdW5nZXIgcGVvcGxlIHdobyBkbyBub3QgZWF0IG91dHNpZGUgZnJlcXVlbnRseSwgaGVpZ2h0IGRldGVybWluZXMgd2hldGhlciB0aGV5IGFyZSBub3JtYWwgd2VpZ2h0IG9yIG92ZXJ3ZWlnaHQuIEZvciBvbGRlciBwZW9wbGUsIHRoZSB0cmVlIGNoZWNrcyBmYW1pbHkgaGlzdG9yeSwgdGVjaG5vbG9neSB1c2UgdGltZSAoVFVFKSwgbnVtYmVyIG9mIG1lYWxzIChOQ1BfMyksIGFuZCBwaHlzaWNhbCBhY3Rpdml0eSAoRkFGKSB0byBtYWtlIHRoZSBmaW5hbCBwcmVkaWN0aW9uLiBUaGUgcHJ1bmVkIHRyZWUgcmVtb3ZlcyBjb21wbGV4IGJyYW5jaGVzIHRoYXQgZG8gbm90IGhlbHAgbXVjaCB3aXRoIHByZWRpY3Rpb24sIG1ha2luZyBpdCBhIGNsZWFuZXIgbW9kZWwgdGhhdCBpcyBiZXR0ZXIgZm9yIHVuZGVyc3RhbmRpbmcgb2Jlc2l0eSBwYXR0ZXJucy4KCiMjIyMgVGVzdCBTZXQgUGVyZm9ybWFuY2UKCmBgYHtyIHRyZWUtZGV2aWFuY2UtZXZhbH0KcHJlZF9jbGFzc19kZXYgPC0gcHJlZGljdCh0cmVlX2RldmlhbmNlX3BydW5lZCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSwgdHlwZSA9ICJjbGFzcyIpCnByZWRfcHJvYl9kZXYgPC0gcHJlZGljdCh0cmVlX2RldmlhbmNlX3BydW5lZCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSwgdHlwZSA9ICJ2ZWN0b3IiKQpjb25mX21hdHJpeF9kZXYgPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZF9jbGFzc19kZXYsIEFjdHVhbCA9IHRlc3RfZGF0YSRPYmVzaXR5XzMpCnByaW50KGNvbmZfbWF0cml4X2RldikKdG90YWxfZXJyb3JfZGV2IDwtIG1lYW4ocHJlZF9jbGFzc19kZXYgIT0gdGVzdF9kYXRhJE9iZXNpdHlfMykKY2xhc3NfZXJyb3JzX2RldiA8LSAxIC0gZGlhZyhjb25mX21hdHJpeF9kZXYpIC8gY29sU3Vtcyhjb25mX21hdHJpeF9kZXYpCnJvY19kZXYgPC0gbXVsdGljbGFzcy5yb2ModGVzdF9kYXRhJE9iZXNpdHlfMywgcHJlZF9wcm9iX2RldikKYXVjX2RldiA8LSBhdWMocm9jX2RldikKCmBgYAoKKipQZXJmb3JtYW5jZSBNZXRyaWNzOioqCgp8IE1ldHJpYyAgICAgICAgICAgICAgICB8ICBWYWx1ZSB8Cnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tOnwKfCBUb3RhbCBFcnJvciBSYXRlICAgICAgfCAzMy42NCUgfAp8IE5vcm1hbF9vcl9VbmRlciBFcnJvciB8ICAzLjEyJSB8CnwgT3ZlcndlaWdodCBFcnJvciAgICAgIHwgNzYuOTIlIHwKfCBPYmVzZSBFcnJvciAgICAgICAgICAgfCA4Mi4zNSUgfAp8IE11bHRpY2xhc3MgQVVDICAgICAgICB8ICAuNjQ4OSB8CgpPbiB0aGUgdGVzdCBzZXQsIHRoZSBtb2RlbCBoYXMgYSB0b3RhbCBlcnJvciByYXRlIG9mIDMzLjY0JSwgbWVhbmluZyBpdCBjb3JyZWN0bHkgcHJlZGljdHMgYWJvdXQgNjYlIG9mIGNhc2VzLiBUaGUgbW9kZWwgcGVyZm9ybXMgd2VsbCBhdCBpZGVudGlmeWluZyBub3JtYWwgd2VpZ2h0IHBlb3BsZSwgd2l0aCBvbmx5IDMuMTIlIGVycm9yLiBIb3dldmVyLCBpdCBzdHJ1Z2dsZXMgd2l0aCBvdmVyd2VpZ2h0IGFuZCBvYmVzZSBncm91cHMsIHdpdGggZXJyb3IgcmF0ZXMgb2YgNzYuOTIlIGFuZCA4Mi4zNSUuIFRoaXMgbWVhbnMgdGhlIG1vZGVsIG9mdGVuIG1pc3Rha2VzIG92ZXJ3ZWlnaHQgYW5kIG9iZXNlIHBlb3BsZSBhcyBub3JtYWwgd2VpZ2h0LiBUaGUgbXVsdGljbGFzcyBBVUMgc2NvcmUgb2YgMC42NSBzaG93cyBtb2RlcmF0ZSBwZXJmb3JtYW5jZS4gVGhlIGNvbmZ1c2lvbiBtYXRyaXggcmV2ZWFscyB0aGF0IG1vc3QgcHJlZGljdGlvbiBlcnJvcnMgY29tZSBmcm9tIGNvbmZ1c2luZyBvdmVyd2VpZ2h0IGFuZCBvYmVzZSBjYXNlcyB3aXRoIG5vcm1hbCB3ZWlnaHQsIHN1Z2dlc3RpbmcgdGhlIG1vZGVsIG5lZWRzIGJldHRlciBhYmlsaXR5IHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gdGhlc2UgaGVhdmllciB3ZWlnaHQgZ3JvdXBzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMgTW9kZWwgMjogR2luaS1CYXNlZCBUcmVlCgpgYGB7ciB0cmVlLWdpbml9CnRyZWVfZ2luaSA8LSB0cmVlKE9iZXNpdHlfMyB+IC4sIGRhdGEgPSB0cmFpbl9kYXRhLCBzcGxpdCA9ICJnaW5pIikKc3VtbWFyeSh0cmVlX2dpbmkpCnNldC5zZWVkKDIwMjQpCmN2X2dpbmkgPC0gY3YudHJlZSh0cmVlX2dpbmksIEZVTiA9IHBydW5lLm1pc2NsYXNzKQpwbG90KGN2X2dpbmkkc2l6ZSwgY3ZfZ2luaSRkZXYsIHR5cGUgPSAiYiIsIHBjaCA9IDE5LCBjb2wgPSAiZGFya2JsdWUiLCBsd2QgPSAyLHhsYWIgPSAiVHJlZSBTaXplIiwgeWxhYiA9ICJDViBNaXNjbGFzc2lmaWNhdGlvbiBFcnJvciIsbWFpbiA9ICJDViBFcnJvciAoR2luaSBTcGxpdCkiKQpncmlkKCkKYmVzdF9zaXplX2dpbmkgPC0gY3ZfZ2luaSRzaXplW3doaWNoLm1pbihjdl9naW5pJGRldildCmNhdCgiXG5PcHRpbWFsIFRyZWUgU2l6ZSAoR2luaSk6IiwgYmVzdF9zaXplX2dpbmksICJcbiIpCnRyZWVfZ2luaV9wcnVuZWQgPC0gcHJ1bmUubWlzY2xhc3ModHJlZV9naW5pLCBiZXN0ID0gYmVzdF9zaXplX2dpbmkpCnBsb3QodHJlZV9naW5pX3BydW5lZCkKdGV4dCh0cmVlX2dpbmlfcHJ1bmVkLCBwcmV0dHkgPSAwLCBjZXggPSAwLjgsIGNvbCA9ICJkYXJrZ3JlZW4iKQp0aXRsZShwYXN0ZSgiUHJ1bmVkIEdpbmkgVHJlZSAoU2l6ZSA9IiwgYmVzdF9zaXplX2dpbmksICIpIikpCnByZWRfY2xhc3NfZ2luaSA8LSBwcmVkaWN0KHRyZWVfZ2luaV9wcnVuZWQsIG5ld2RhdGEgPSB0ZXN0X2RhdGEsIHR5cGUgPSAiY2xhc3MiKQpwcmVkX3Byb2JfZ2luaSA8LSBwcmVkaWN0KHRyZWVfZ2luaV9wcnVuZWQsIG5ld2RhdGEgPSB0ZXN0X2RhdGEsIHR5cGUgPSAidmVjdG9yIikKY29uZl9tYXRyaXhfZ2luaSA8LSB0YWJsZShQcmVkaWN0ZWQgPSBwcmVkX2NsYXNzX2dpbmksIEFjdHVhbCA9IHRlc3RfZGF0YSRPYmVzaXR5XzMpCnByaW50KGNvbmZfbWF0cml4X2dpbmkpCnRvdGFsX2Vycm9yX2dpbmkgPC0gbWVhbihwcmVkX2NsYXNzX2dpbmkgIT0gdGVzdF9kYXRhJE9iZXNpdHlfMykKY2xhc3NfZXJyb3JzX2dpbmkgPC0gMSAtIGRpYWcoY29uZl9tYXRyaXhfZ2luaSkgLyBjb2xTdW1zKGNvbmZfbWF0cml4X2dpbmkpCnJvY19naW5pIDwtIG11bHRpY2xhc3Mucm9jKHRlc3RfZGF0YSRPYmVzaXR5XzMsIHByZWRfcHJvYl9naW5pKQphdWNfZ2luaSA8LSBhdWMocm9jX2dpbmkpCmBgYAoKKipQZXJmb3JtYW5jZSBNZXRyaWNzOioqCgp8IE1ldHJpYyAgICAgICAgICAgICAgICB8ICBWYWx1ZSB8Cnw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tOnwKfCBUb3RhbCBFcnJvciBSYXRlICAgICAgfCAzMy42NCUgfAp8IE5vcm1hbF9vcl9VbmRlciBFcnJvciB8IDE0LjA2JSB8CnwgT3ZlcndlaWdodCBFcnJvciAgICAgIHwgNjkuMjMlIHwKfCBPYmVzZSBFcnJvciAgICAgICAgICAgfCA1Mi45NCUgfAp8IE11bHRpY2xhc3MgQVVDICAgICAgICB8IDAuNzM3NyB8CgpBIHNlY29uZCB0cmVlIG1vZGVsIHdhcyBidWlsdCB1c2luZyBHaW5pIGluc3RlYWQgb2YgZGV2aWFuY2UgYXMgdGhlIHNwbGl0dGluZyBjcml0ZXJpb24uIFRoZSBHaW5pIHRyZWUgY3JlYXRlZCA1MyBub2RlcyBiZWZvcmUgcHJ1bmluZywgd2l0aCBhIHRyYWluaW5nIGVycm9yIHJhdGUgb2YgMjAuNTIlLCB3aGljaCBpcyBiZXR0ZXIgdGhhbiB0aGUgZGV2aWFuY2UgdHJlZS4gQ3Jvc3MtdmFsaWRhdGlvbiBzaG93ZWQgdGhhdCB0aGUgb3B0aW1hbCB0cmVlIHNpemUgaXMgMjMgbm9kZXMuIFRoaXMgbGFyZ2VyIHRyZWUgdXNlcyBtb3JlIHZhcmlhYmxlcyBpbmNsdWRpbmcgd2F0ZXIgaW50YWtlLCBmYW1pbHkgaGlzdG9yeSwgZ2VuZGVyLCB0cmFuc3BvcnRhdGlvbiBtZXRob2QgKE1UUkFOU18zKSwgYW5kIHZlZ2V0YWJsZSBjb25zdW1wdGlvbiAoRkNWQyksIHNob3dpbmcgdGhhdCBHaW5pIGNvbnNpZGVycyBkaWZmZXJlbnQgZmFjdG9ycyBpbXBvcnRhbnQuIE9uIHRoZSB0ZXN0IHNldCwgdGhlIEdpbmkgdHJlZSBoYXMgdGhlIHNhbWUgdG90YWwgZXJyb3IgcmF0ZSBvZiAzMy42NCUgYXMgdGhlIGRldmlhbmNlIHRyZWUsIGJ1dCB0aGUgZXJyb3IgZGlzdHJpYnV0aW9uIGlzIGRpZmZlcmVudC4gSXQgaGFzIGhpZ2hlciBlcnJvciBmb3Igbm9ybWFsIHdlaWdodCBwZW9wbGUgKDE0LjA2JSkgYnV0IGxvd2VyIGVycm9ycyBmb3Igb3ZlcndlaWdodCAoNjkuMjMlKSBhbmQgb2Jlc2UgKDUyLjk0JSkgZ3JvdXBzLiBUaGUgbXVsdGljbGFzcyBBVUMgb2YgMC43NCBpcyBiZXR0ZXIgdGhhbiB0aGUgZGV2aWFuY2UgdHJlZSdzIDAuNjUsIGluZGljYXRpbmcgdGhlIEdpbmkgdHJlZSBpcyBtb3JlIGVmZmVjdGl2ZSBhdCBkaXN0aW5ndWlzaGluZyBiZXR3ZWVuIG9iZXNpdHkgY2xhc3Nlcy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgQmFnZ2luZyBSZXN1bHRzCgpgYGB7ciBiYWdnaW5nLWZpdH0Kc2V0LnNlZWQoMjA4KQpwIDwtIGxlbmd0aChhbGxvd2VkX3ByZWRpY3RvcnMpCmJhZ19tb2RlbCA8LSByYW5kb21Gb3Jlc3QoT2Jlc2l0eV8zIH4gLiwgZGF0YSA9IHRyYWluX2RhdGEsbXRyeSA9IHAsbnRyZWUgPSA1MDAsIGltcG9ydGFuY2UgPVRSVUUpCmNhdCgiLS0tIEJhZ2dpbmcgTW9kZWwgU3VtbWFyeSAtLS1cbiIpCnByaW50KGJhZ19tb2RlbCkKYGBgCgpCYWdnaW5nIHdhcyB1c2VkIHRvIGltcHJvdmUgcHJlZGljdGlvbnMgYnkgY29tYmluaW5nIDUwMCBkZWNpc2lvbiB0cmVlcy4gSW4gYmFnZ2luZywgZWFjaCB0cmVlIGlzIGJ1aWx0IGZyb20gYSByYW5kb20gc2FtcGxlIG9mIHRoZSB0cmFpbmluZyBkYXRhLCBhbmQgcHJlZGljdGlvbnMgYXJlIG1hZGUgYnkgYXZlcmFnaW5nIHJlc3VsdHMgYWNyb3NzIGFsbCB0cmVlcy4gVGhlIGJhZ2dpbmcgbW9kZWwgdXNlcyBhbGwgMTUgcHJlZGljdG9yIHZhcmlhYmxlcyBhdCBlYWNoIHNwbGl0LiBUaGUgb3V0LW9mLWJhZyAoT09CKSBlcnJvciByYXRlIGlzIDMxLjYlLCB3aGljaCBpcyBsb3dlciB0aGFuIHRoZSBzaW5nbGUgdHJlZSBtb2RlbHMuIFRoZSBlcnJvciByYXRlcyBmb3IgZWFjaCBjbGFzcyBzaG93IHRoYXQgYmFnZ2luZyBwZXJmb3JtcyB3ZWxsIGZvciBub3JtYWwgd2VpZ2h0IHBlb3BsZSAoOS41JSBlcnJvcikgYnV0IHN0aWxsIHN0cnVnZ2xlcyB3aXRoIG92ZXJ3ZWlnaHQgKDU4LjUlIGVycm9yKSBhbmQgb2Jlc2UgKDg1LjUlIGVycm9yKSBncm91cHMuIEJhZ2dpbmcgcmVkdWNlcyBvdmVyZml0dGluZyBjb21wYXJlZCB0byBzaW5nbGUgdHJlZXMsIGJ1dCB0aGUgY2xhc3MgaW1iYWxhbmNlIHByb2JsZW0gcmVtYWluc+KAlHRoZSBtb2RlbCBpcyBnb29kIGF0IGlkZW50aWZ5aW5nIG5vcm1hbCB3ZWlnaHQgcGVvcGxlIGJ1dCBtYWtlcyBtYW55IG1pc3Rha2VzIG9uIGhlYXZpZXIgZ3JvdXBzLgoKIyMjIFZhcmlhYmxlIEltcG9ydGFuY2UKCmBgYHtyIGJhZ2dpbmctaW1wb3J0YW5jZX0KcGFyKG1mcm93ID0gYygxLCAyKSkKdmFySW1wUGxvdChiYWdfbW9kZWwsIG1haW4gPSAiQmFnZ2luZzogVmFyaWFibGUgSW1wb3J0YW5jZSIpCnBhcihtZnJvdyA9IGMoMSwgMSkpCmNhdCgiXG4tLS0gVmFyaWFibGUgSW1wb3J0YW5jZSAtLS1cbiIpCmltcG9ydGFuY2UoYmFnX21vZGVsKQpgYGAKCioqSW50ZXJwcmV0YXRpb246KioKCkFnZSBhbmQgaGVpZ2h0IGFyZSB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzLCByYW5raW5nIGhpZ2hlc3QgaW4gYm90aCBhY2N1cmFjeSBkZWNyZWFzZSBhbmQgR2luaSBkZWNyZWFzZSBtZWFzdXJlcy4gV2F0ZXIgaW50YWtlIChDSDJPKSBhbmQgZWF0aW5nIGZyZXF1ZW5jeSAoQ0FFQykgYXJlIGFsc28gaW1wb3J0YW50LCBlc3BlY2lhbGx5IGZvciBwcmVkaWN0aW5nIG9iZXNlIGNhc2VzLiBGYW1pbHkgaGlzdG9yeSBvZiBvdmVyd2VpZ2h0IG1hdHRlcnMgbW9yZSBmb3Igb2Jlc2UgcHJlZGljdGlvbiB0aGFuIGZvciBub3JtYWwgd2VpZ2h0IHByZWRpY3Rpb24uIFBoeXNpY2FsIGFjdGl2aXR5IChGQUYpIGlzIHVzZWZ1bCBmb3IgcHJlZGljdGluZyBvYmVzZSBjYXNlcy4gR2VuZGVyIGFuZCBzbW9raW5nIGFyZSBsZXNzIGltcG9ydGFudCBvdmVyYWxsLiBUaGUgaW1wb3J0YW5jZSByYW5raW5ncyBzaG93IHRoYXQgZGVtb2dyYXBoaWMgZmFjdG9ycyBsaWtlIGFnZSBhbmQgaGVpZ2h0IGFyZSB0aGUgc3Ryb25nZXN0IHByZWRpY3RvcnMgb2Ygb2Jlc2l0eSwgZm9sbG93ZWQgYnkgYmVoYXZpb3JhbCBmYWN0b3JzIGxpa2Ugd2F0ZXIgaW50YWtlIGFuZCBlYXRpbmcgaGFiaXRzLiBUaGlzIG1hdGNoZXMgd2hhdCB0aGUgc2luZ2xlIHRyZWVzIHNob3dlZOKAlGFnZSBpcyBhbHdheXMgdGhlIGZpcnN0IHNwbGl0LCBzdWdnZXN0aW5nIGl0IGlzIHRoZSBzdHJvbmdlc3QgZGl2aWRpbmcgZmFjdG9yIGZvciBvYmVzaXR5IGNsYXNzaWZpY2F0aW9uLgoKIyMjIFRlc3QgU2V0IFBlcmZvcm1hbmNlCgpgYGB7ciBiYWdnaW5nLWV2YWx9CnByZWRfYmFnIDwtIHByZWRpY3QoYmFnX21vZGVsLCBuZXdkYXRhID0gdGVzdF9kYXRhLCB0eXBlID0gImNsYXNzIikKcHJlZF9wcm9iX2JhZyA8LSBwcmVkaWN0KGJhZ19tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSwgdHlwZSA9ICJwcm9iIikKY29uZl9tYXRyaXhfYmFnIDwtIHRhYmxlKFByZWRpY3RlZCA9IHByZWRfYmFnLCBBY3R1YWwgPSB0ZXN0X2RhdGEkT2Jlc2l0eV8zKQpwcmludChjb25mX21hdHJpeF9iYWcpCnRvdGFsX2Vycm9yX2JhZyA8LSBtZWFuKHByZWRfYmFnICE9IHRlc3RfZGF0YSRPYmVzaXR5XzMpCmNsYXNzX2Vycm9yc19iYWcgPC0gMSAtIGRpYWcoY29uZl9tYXRyaXhfYmFnKSAvIGNvbFN1bXMoY29uZl9tYXRyaXhfYmFnKQpyb2NfYmFnIDwtIG11bHRpY2xhc3Mucm9jKHRlc3RfZGF0YSRPYmVzaXR5XzMsIHByZWRfcHJvYl9iYWcpCmF1Y19iYWcgPC0gYXVjKHJvY19iYWcpCmBgYAoKKipQZXJmb3JtYW5jZSBNZXRyaWNzOioqCgp8IE1ldHJpYyAgICAgICAgICAgICAgICAgICAgfCAgVmFsdWUgfAp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS06fAp8IE9PQiBFcnJvciBSYXRlIChUcmFpbmluZykgfCAgMzEuNiUgfAp8IFRvdGFsIEVycm9yIFJhdGUgICAgICAgICAgfCAzNC41OCUgfAp8IE5vcm1hbF9vcl9VbmRlciBFcnJvciAgICAgfCAgOS4zOCUgfAp8IE92ZXJ3ZWlnaHQgRXJyb3IgICAgICAgICAgfCA3Ni45MiUgfAp8IE9iZXNlIEVycm9yICAgICAgICAgICAgICAgfCA2NC43MSUgfAp8IE11bHRpY2xhc3MgQVVDICAgICAgICAgICAgfCAwLjcyMDcgfAoKT24gdGhlIHRlc3Qgc2V0LCBiYWdnaW5nIGhhcyBhIHRvdGFsIGVycm9yIHJhdGUgb2YgMzQuNTglLCB3aGljaCBpcyBzbGlnaHRseSB3b3JzZSB0aGFuIHRoZSB0cmFpbmluZyBPT0IgZXJyb3Igb2YgMzEuNiUsIHN1Z2dlc3Rpbmcgc29tZSBvdmVyZml0dGluZy4gVGhlIG1vZGVsIHBlcmZvcm1zIHdlbGwgb24gbm9ybWFsIHdlaWdodCBwZW9wbGUgd2l0aCBvbmx5IDkuMzglIGVycm9yLCBidXQgc3RydWdnbGVzIHdpdGggb3ZlcndlaWdodCAoNzYuOTIlIGVycm9yKSBhbmQgb2Jlc2UgKDY0LjcxJSBlcnJvcikgZ3JvdXBzLiBUaGUgbXVsdGljbGFzcyBBVUMgb2YgMC43MiBpcyBnb29kIGFuZCBtYXRjaGVzIHRoZSBHaW5pIHRyZWUncyBwZXJmb3JtYW5jZS4gQ29tcGFyZWQgdG8gc2luZ2xlIHRyZWUgbW9kZWxzLCBiYWdnaW5nIG1ha2VzIGZld2VyIG1pc3Rha2VzIG9uIG9iZXNlIGNhc2VzICg2NC43MSUgdnMgODIuMzUlIGZvciBkZXZpYW5jZSB0cmVlLCA1Mi45NCUgZm9yIEdpbmkgdHJlZSkgYnV0IGhhcyBoaWdoZXIgZXJyb3Igb24gbm9ybWFsIHdlaWdodCBjYXNlcyAoOS4zOCUgdnMgMy4xMiUgZm9yIGRldmlhbmNlIHRyZWUsIDE0LjA2JSBmb3IgR2luaSB0cmVlKS4gT3ZlcmFsbCwgYmFnZ2luZyBpbXByb3ZlcyBwcmVkaWN0aW9ucyBmb3IgaGFyZGVyLXRvLWNsYXNzaWZ5IGdyb3VwcyBsaWtlIG9iZXNlLCBidXQgdGhlIGNsYXNzIGltYmFsYW5jZSBpc3N1ZSByZW1haW5z4oCUdGhlIG1vZGVsIGlzIHN0aWxsIGJldHRlciBhdCBpZGVudGlmeWluZyBub3JtYWwgd2VpZ2h0IHBlb3BsZSB0aGFuIGhlYXZpZXIgZ3JvdXBzLgoKIyMgUmFuZG9tIEZvcmVzdCBSZXN1bHRzCgpgYGB7ciByZi1maXR9CnNldC5zZWVkKDIwOCkKcCA8LSBsZW5ndGgoYWxsb3dlZF9wcmVkaWN0b3JzKQptdHJ5X3JmIDwtIGZsb29yKHNxcnQocCkpCmNhdCgiTnVtYmVyIG9mIHByZWRpY3RvcnMgKHApOiIsIHAsICJcbiIpCmNhdCgibXRyeSBmb3IgUmFuZG9tIEZvcmVzdDogICIsIG10cnlfcmYsICJcblxuIikKcmZfbW9kZWwgPC0gcmFuZG9tRm9yZXN0KAogIE9iZXNpdHlfMyB+IC4sIAogIGRhdGEgPSB0cmFpbl9kYXRhLAogIG10cnkgPSBtdHJ5X3JmLAogIG50cmVlID0gNTAwLCAKICBpbXBvcnRhbmNlID0gVFJVRQopCmNhdCgiLS0tIFJhbmRvbSBGb3Jlc3QgTW9kZWwgU3VtbWFyeSAtLS1cbiIpCnByaW50KHJmX21vZGVsKQpgYGAKClJhbmRvbSBmb3Jlc3Qgd2FzIGJ1aWx0IGJ5IHVzaW5nIG9ubHkgMyByYW5kb20gdmFyaWFibGVzIGF0IGVhY2ggc3BsaXQgaW5zdGVhZCBvZiBhbGwgMTUgdmFyaWFibGVzIGxpa2UgaW4gYmFnZ2luZy4gVGhpcyBmb3JjZXMgdGhlIG1vZGVsIHRvIGNvbnNpZGVyIGRpZmZlcmVudCB2YXJpYWJsZXMgYW5kIHJlZHVjZXMgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdHJlZXMsIHdoaWNoIGNhbiBpbXByb3ZlIHByZWRpY3Rpb25zLiBUaGUgcmFuZG9tIGZvcmVzdCBjcmVhdGVkIDUwMCB0cmVlcyB3aXRoIGFuIE9PQiBlcnJvciByYXRlIG9mIDMxLjEzJSwgd2hpY2ggaXMgc2xpZ2h0bHkgYmV0dGVyIHRoYW4gYmFnZ2luZydzIDMxLjYlLiBUaGUgZXJyb3IgcmF0ZXMgYnkgY2xhc3Mgc2hvdyB0aGF0IHJhbmRvbSBmb3Jlc3QgcGVyZm9ybXMgd2VsbCBvbiBub3JtYWwgd2VpZ2h0IHBlb3BsZSAoNi4wOCUgZXJyb3IpLCBzaW1pbGFyIHRvIGJhZ2dpbmcuIEhvd2V2ZXIsIGVycm9yIHJhdGVzIGZvciBvdmVyd2VpZ2h0ICg2Ni45OCUgZXJyb3IpIGFuZCBvYmVzZSAoODEuODIlIGVycm9yKSBncm91cHMgYXJlIGhpZ2hlciB0aGFuIGJhZ2dpbmcsIHN1Z2dlc3RpbmcgdGhhdCBsaW1pdGluZyB2YXJpYWJsZXMgdG8gMyBwZXIgc3BsaXQgbWFrZXMgaXQgaGFyZGVyIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gaGVhdmllciBncm91cHMuIFwjIyMgVmFyaWFibGUgSW1wb3J0YW5jZQoKYGBge3IgcmYtaW1wb3J0YW5jZX0KdmFySW1wUGxvdChyZl9tb2RlbCwgbWFpbiA9ICJSYW5kb20gRm9yZXN0OiBWYXJpYWJsZSBJbXBvcnRhbmNlIikKY2F0KCJcbi0tLSBWYXJpYWJsZSBJbXBvcnRhbmNlIC0tLVxuIikKaW1wb3J0YW5jZShyZl9tb2RlbCkKYGBgCgoqKkludGVycHJldGF0aW9uOioqIFRoZSB2YXJpYWJsZSBpbXBvcnRhbmNlIGluIHJhbmRvbSBmb3Jlc3Qgc2hvd3Mgc2ltaWxhciBwYXR0ZXJucyB0byBiYWdnaW5nLCB3aXRoIGFnZSBhbmQgaGVpZ2h0IGFzIHRoZSB0b3AgZmFjdG9ycy4gSG93ZXZlciwgdGhlIHJhbmtpbmdzIGNoYW5nZSBzbGlnaHRseSBiZWNhdXNlIHJhbmRvbSBmb3Jlc3Qgb25seSBjb25zaWRlcnMgMyB2YXJpYWJsZXMgcGVyIHNwbGl0LiBXYXRlciBpbnRha2UgKENIMk8pIGJlY29tZXMgbW9yZSBpbXBvcnRhbnQgaW4gcmFuZG9tIGZvcmVzdCwgc3VnZ2VzdGluZyB0aGF0IHdoZW4gYWdlIGFuZCBoZWlnaHQgYXJlIG5vdCBhdmFpbGFibGUgaW4gYSBzcGxpdCwgd2F0ZXIgaW50YWtlIGlzIGEgdXNlZnVsIGFsdGVybmF0aXZlLiBFYXRpbmcgZnJlcXVlbmN5IChDQUVDKSwgZmFtaWx5IGhpc3RvcnksIGFuZCBwaHlzaWNhbCBhY3Rpdml0eSAoRkFGKSByZW1haW4gaW1wb3J0YW50LgoKIyMjIFRlc3QgU2V0IFBlcmZvcm1hbmNlCgpgYGB7ciByZi1ldmFsfQpwcmVkX3JmIDwtIHByZWRpY3QocmZfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0X2RhdGEsIHR5cGUgPSAiY2xhc3MiKQpwcmVkX3Byb2JfcmYgPC0gcHJlZGljdChyZl9tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfZGF0YSwgdHlwZSA9ICJwcm9iIikKY29uZl9tYXRyaXhfcmYgPC0gdGFibGUoUHJlZGljdGVkID0gcHJlZF9yZiwgQWN0dWFsID0gdGVzdF9kYXRhJE9iZXNpdHlfMykKcHJpbnQoY29uZl9tYXRyaXhfcmYpCgp0b3RhbF9lcnJvcl9yZiAgPC0gbWVhbihwcmVkX3JmICE9IHRlc3RfZGF0YSRPYmVzaXR5XzMpCgpjbGFzc19lcnJvcnNfcmYgPC0gMSAtIGRpYWcoY29uZl9tYXRyaXhfcmYpIC8gY29sU3Vtcyhjb25mX21hdHJpeF9yZikKCnJvY19yZiA8LSBtdWx0aWNsYXNzLnJvYyh0ZXN0X2RhdGEkT2Jlc2l0eV8zLCBwcmVkX3Byb2JfcmYpCmF1Y19yZiA8LSBhdWMocm9jX3JmKQpgYGAKCioqUGVyZm9ybWFuY2UgTWV0cmljczoqKgoKfCBNZXRyaWMgICAgICAgICAgICAgICAgICAgIHwgIFZhbHVlIHwKfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tOnwKfCBPT0IgRXJyb3IgUmF0ZSAoVHJhaW5pbmcpIHwgMzEuMTMlIHwKfCBUb3RhbCBFcnJvciBSYXRlICAgICAgICAgIHwgMzEuNzglIHwKfCBOb3JtYWxfb3JfVW5kZXIgRXJyb3IgICAgIHwgIDMuMTIlIHwKfCBPdmVyd2VpZ2h0IEVycm9yICAgICAgICAgIHwgODAuNzclIHwKfCBPYmVzZSBFcnJvciAgICAgICAgICAgICAgIHwgNjQuNzElIHwKfCBNdWx0aWNsYXNzIEFVQyAgICAgICAgICAgIHwgMC43NTAzIHwKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgTW9kZWwgQ29tcGFyaXNvbgoKIyMjIFN1bW1hcnkgVGFibGUKCmBgYHtyIGNvbXBhcmlzb24tdGFibGV9CnN1bW1hcnlfZGYgPC0gZGF0YS5mcmFtZSgKICBNb2RlbCA9IGMoIlRyZWUgKERldmlhbmNlKSIsICJUcmVlIChHaW5pKSIsICJCYWdnaW5nIiwgIlJhbmRvbSBGb3Jlc3QiKSwKICBUb3RhbF9FcnJvciA9IGModG90YWxfZXJyb3JfZGV2LCB0b3RhbF9lcnJvcl9naW5pLCB0b3RhbF9lcnJvcl9iYWcsIHRvdGFsX2Vycm9yX3JmKSwKICBOb3JtYWxfRXJyb3IgPSBjKGNsYXNzX2Vycm9yc19kZXZbIk5vcm1hbF9vcl9VbmRlciJdLCBjbGFzc19lcnJvcnNfZ2luaVsiTm9ybWFsX29yX1VuZGVyIl0sIGNsYXNzX2Vycm9yc19iYWdbIk5vcm1hbF9vcl9VbmRlciJdLCBjbGFzc19lcnJvcnNfcmZbIk5vcm1hbF9vcl9VbmRlciJdKSwKICBPdmVyd2VpZ2h0X0Vycm9yID0gYyhjbGFzc19lcnJvcnNfZGV2WyJPdmVyd2VpZ2h0Il0sIGNsYXNzX2Vycm9yc19naW5pWyJPdmVyd2VpZ2h0Il0sIGNsYXNzX2Vycm9yc19iYWdbIk92ZXJ3ZWlnaHQiXSwgY2xhc3NfZXJyb3JzX3JmWyJPdmVyd2VpZ2h0Il0pLAogIE9iZXNlX0Vycm9yID0gYyhjbGFzc19lcnJvcnNfZGV2WyJPYmVzZSJdLCBjbGFzc19lcnJvcnNfZ2luaVsiT2Jlc2UiXSwgY2xhc3NfZXJyb3JzX2JhZ1siT2Jlc2UiXSwgY2xhc3NfZXJyb3JzX3JmWyJPYmVzZSJdKSwKICBBVUMgPSBjKGF1Y19kZXYsIGF1Y19naW5pLCBhdWNfYmFnLCBhdWNfcmYpKQojIDQuIEZvcm1hdCBmb3IgZGlzcGxheQpzdW1tYXJ5X2RmX3ByaW50IDwtIHN1bW1hcnlfZGYKc3VtbWFyeV9kZl9wcmludFssIDI6NV0gPC0gbGFwcGx5KHN1bW1hcnlfZGZfcHJpbnRbLCAyOjVdLCBmdW5jdGlvbih4KSBwYXN0ZTAocm91bmQoeCAqIDEwMCwgMSksICIlIikpCnN1bW1hcnlfZGZfcHJpbnQkQVVDIDwtIHJvdW5kKHN1bW1hcnlfZGYkQVVDLCA0KQpwcmludChzdW1tYXJ5X2RmX3ByaW50KQpgYGAKCiMjIyBDb21wYXJpc29uIFZpc3VhbGl6YXRpb24KCmBgYHtyIGNvbXBhcmlzb24tcGxvdH0KZXJyb3JfbWF0cml4IDwtIGFzLm1hdHJpeChzdW1tYXJ5X2RmWywgYygiVG90YWxfRXJyb3IiLCAiTm9ybWFsX0Vycm9yIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT3ZlcndlaWdodF9FcnJvciIsICJPYmVzZV9FcnJvciIpXSkKcm93bmFtZXMoZXJyb3JfbWF0cml4KSA8LSBzdW1tYXJ5X2RmJE1vZGVsCmNvbF9wYWxldHRlIDwtIGMoIiNENzMwMjciLCAiI0ZDOEQ1OSIsICIjRkVFMDkwIiwgIiM5MUJGREIiKQpwYXIobWFyID0gYyg3LCA0LCA0LCA4KSwgeHBkID0gVFJVRSkKYmFycGxvdCh0KGVycm9yX21hdHJpeCkgKiAxMDAsIGJlc2lkZSA9IFRSVUUsY29sID0gY29sX3BhbGV0dGUsbWFpbiA9ICJNb2RlbCBFcnJvciBDb21wYXJpc29uIix5bGFiID0gIkVycm9yIFJhdGUgKCUpIix5bGltID0gYygwLCBtYXgoZXJyb3JfbWF0cml4KSAqIDEyMCksbGFzID0gMixjZXgubmFtZXMgPSAwLjgpCmxlZ2VuZCgidG9wcmlnaHQiLCBpbnNldCA9IGMoLTAuMjUsIDApLGxlZ2VuZCA9IGMoIlRvdGFsIiwgIk5vcm1hbCIsICJPdmVyd2VpZ2h0IiwgIk9iZXNlIiksCmZpbGwgPSBjb2xfcGFsZXR0ZSx0aXRsZSA9ICJFcnJvciBUeXBlIixjZXggPSAwLjgsYnR5ID0gIm4iKQpwYXIobWFyID0gYyg1LCA0LCA0LCAyKSwgeHBkID0gRkFMU0UpCnBhcihtYXIgPSBjKDcsIDQsIDQsIDIpKQpiYXJwbG90KHN1bW1hcnlfZGYkQVVDLG5hbWVzLmFyZyA9IHN1bW1hcnlfZGYkTW9kZWwsY29sID0gYygiY29yYWwiLCAib3JhbmdlIiwgInN0ZWVsYmx1ZSIsICJkYXJrYmx1ZSIpLG1haW4gPSAiTW9kZWwgQVVDIENvbXBhcmlzb24iLHlsYWIgPSAiQVVDIix5bGltID0gYygwLCAxKSxsYXMgPSAyLGNleC5uYW1lcyA9IDAuOCkKYWJsaW5lKGggPSAwLjUsIGx0eSA9IDIsIGNvbCA9ICJkYXJrZ3JheSIpCnBhcihtYXIgPSBjKDUsIDQsIDQsIDIpKQpiZXN0X2lkeCA8LSB3aGljaC5taW4oc3VtbWFyeV9kZiRUb3RhbF9FcnJvcikKYmVzdF9tb2RlbCA8LSBzdW1tYXJ5X2RmJE1vZGVsW2Jlc3RfaWR4XQpjYXQoIlxuPT09PT0gQkVTVCBNT0RFTCBTRUxFQ1RJT04gPT09PT1cblxuIikKY2F0KCJMb3dlc3QgVG90YWwgRXJyb3I6ICAgICIsIHN1bW1hcnlfZGYkTW9kZWxbd2hpY2gubWluKHN1bW1hcnlfZGYkVG90YWxfRXJyb3IpXSwgCiAgICAiKCIsIHJvdW5kKG1pbihzdW1tYXJ5X2RmJFRvdGFsX0Vycm9yKSAqIDEwMCwgMSksICIlKVxuIikKY2F0KCJIaWdoZXN0IEFVQzogICAgICAgICAgICIsIHN1bW1hcnlfZGYkTW9kZWxbd2hpY2gubWF4KHN1bW1hcnlfZGYkQVVDKV0sIAogICAgIigiLCByb3VuZChtYXgoc3VtbWFyeV9kZiRBVUMpLCA0KSwgIilcbiIpCmNhdCgiQmVzdCBPdmVyd2VpZ2h0IENsYXNzOiAiLCBzdW1tYXJ5X2RmJE1vZGVsW3doaWNoLm1pbihzdW1tYXJ5X2RmJE92ZXJ3ZWlnaHRfRXJyb3IpXSwgCiAgICAiKCIsIHJvdW5kKG1pbihzdW1tYXJ5X2RmJE92ZXJ3ZWlnaHRfRXJyb3IpICogMTAwLCAxKSwgIiUpXG4iKQpjYXQoIlxuPj4gU2VsZWN0ZWQgQmVzdCBNb2RlbDoiLCBiZXN0X21vZGVsLCAiXG4iKQoKYGBgCgojIyMgQmVzdCBNb2RlbCBTZWxlY3Rpb24KCkNvbXBhcmluZyBhbGwgZm91ciBtb2RlbHMgb24gdGhlIHRlc3Qgc2V0LCByYW5kb20gZm9yZXN0IHBlcmZvcm1zIGJlc3Qgd2l0aCBhIHRvdGFsIGVycm9yIHJhdGUgb2YgMzEuOCUgYW5kIHRoZSBoaWdoZXN0IEFVQyBvZiAwLjc1LiBSYW5kb20gZm9yZXN0IGNvcnJlY3RseSBpZGVudGlmaWVzIG5vcm1hbCB3ZWlnaHQgcGVvcGxlIHdpdGggMy4xJSBlcnJvciwgdGhlIHNhbWUgYXMgdGhlIGRldmlhbmNlIHRyZWUuIEZvciBvYmVzZSBjYXNlcywgcmFuZG9tIGZvcmVzdCBoYXMgNjQuNyUgZXJyb3IsIG1hdGNoaW5nIGJhZ2dpbmcuIFRoZSBHaW5pIHRyZWUgcGVyZm9ybXMgYmV0dGVyIG9uIG92ZXJ3ZWlnaHQgY2FzZXMgKDY5LjIlIGVycm9yKSBjb21wYXJlZCB0byByYW5kb20gZm9yZXN0ICg4MC44JSBlcnJvcikuIE92ZXJhbGwsIHJhbmRvbSBmb3Jlc3QgYWNoaWV2ZXMgdGhlIGJlc3QgYmFsYW5jZSBiZXR3ZWVuIHRvdGFsIGFjY3VyYWN5IGFuZCBkaXNjcmltaW5hdGlvbiBiZXR3ZWVuIGNsYXNzZXMsIG1ha2luZyBpdCB0aGUgYmVzdCBtb2RlbCBmb3IgdGhpcyBvYmVzaXR5IGNsYXNzaWZpY2F0aW9uIHRhc2suIFRoZSBkZXZpYW5jZSB0cmVlIGlzIHNpbXBsZSBhbmQgaW50ZXJwcmV0YWJsZSB3aXRoIDEwIG5vZGVzLCBidXQgaGFzIHRoZSBsb3dlc3QgQVVDICgwLjY1KS4gVGhlIEdpbmkgdHJlZSBpcyBtb3JlIGNvbXBsZXggd2l0aCAyMyBub2RlcyBhbmQgcGVyZm9ybXMgYmV0dGVyIG9uIG92ZXJ3ZWlnaHQgY2xhc3NpZmljYXRpb24gYnV0IHdvcnNlIG92ZXJhbGwuIEJhZ2dpbmcgdXNlcyBhbGwgdmFyaWFibGVzIGFuZCBhY2hpZXZlcyBtb2RlcmF0ZSBwZXJmb3JtYW5jZSB3aXRoIEFVQyBvZiAwLjcyLiBSYW5kb20gZm9yZXN0IGJhbGFuY2VzIHNpbXBsaWNpdHkgYW5kIHBlcmZvcm1hbmNlIGJ5IHVzaW5nIG9ubHkgMyB2YXJpYWJsZXMgcGVyIHNwbGl0LCBhY2hpZXZpbmcgdGhlIGxvd2VzdCB0b3RhbCBlcnJvciAoMzEuOCUpIGFuZCBoaWdoZXN0IEFVQyAoMC43NSkuIFRoZSBtYWluIGNoYWxsZW5nZSBhY3Jvc3MgYWxsIG1vZGVscyBpcyBjbGFzc2lmeWluZyBvdmVyd2VpZ2h0IGFuZCBvYmVzZSBjYXNlcywgYXMgdGhlc2UgZ3JvdXBzIHNoYXJlIG1hbnkgY2hhcmFjdGVyaXN0aWNzLiBEZW1vZ3JhcGhpYyBmYWN0b3JzIGxpa2UgYWdlIGFuZCBoZWlnaHQgYXJlIGNvbnNpc3RlbnRseSB0aGUgbW9zdCBpbXBvcnRhbnQgcHJlZGljdG9ycywgZm9sbG93ZWQgYnkgYmVoYXZpb3JhbCBmYWN0b3JzIHN1Y2ggYXMgd2F0ZXIgaW50YWtlIGFuZCBlYXRpbmcgZnJlcXVlbmN5LiBSYW5kb20gZm9yZXN0IGlzIHNlbGVjdGVkIGFzIHRoZSBiZXN0IG1vZGVsIGJlY2F1c2UgaXQgb2ZmZXJzIHRoZSBiZXN0IHRyYWRlLW9mZiBiZXR3ZWVuIGFjY3VyYWN5LCBBVUMgcGVyZm9ybWFuY2UsIGFuZCBnZW5lcmFsaXphdGlvbiB0byBuZXcgZGF0YS4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIEJhZ2dpbmcgdnMgUmFuZG9tIEZvcmVzdDogRGlyZWN0IENvbXBhcmlzb24KCmBgYHtyIGJhZy12cy1yZi1wbG90fQpwYXIobWZyb3cgPSBjKDEsIDEpKQoKcGxvdCgxOjUwMCwgYmFnX21vZGVsJGVyci5yYXRlWywgIk9PQiJdLCB0eXBlID0gImwiLCBjb2wgPSAicmVkIiwgbHdkID0gMix5bGltID0gcmFuZ2UoYyhiYWdfbW9kZWwkZXJyLnJhdGVbLCAiT09CIl0sIHJmX21vZGVsJGVyci5yYXRlWywgIk9PQiJdKSkseGxhYiA9ICJOdW1iZXIgb2YgVHJlZXMiLCB5bGFiID0gIk9PQiBFcnJvciBSYXRlIixtYWluID0gIkJhZ2dpbmcgdnMgUmFuZG9tIEZvcmVzdDogT09CIEVycm9yIENvbnZlcmdlbmNlIikKbGluZXMoMTo1MDAsIHJmX21vZGVsJGVyci5yYXRlWywgIk9PQiJdLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpCmxlZ2VuZCgidG9wcmlnaHQiLCBsZWdlbmQgPSBjKHBhc3RlMCgiQmFnZ2luZyAobXRyeT0iLCBwLCAiKSIpLCBwYXN0ZTAoIlJhbmRvbSBGb3Jlc3QgKG10cnk9IiwgbXRyeV9yZiwgIikiKSksY29sID0gYygicmVkIiwgImJsdWUiKSwgbHdkID0gMixidHkgPSAibiIpCmdyaWQoKQpgYGAKClRoZSBPT0IgZXJyb3IgY29udmVyZ2VuY2UgcGxvdCBzaG93cyBob3cgYmFnZ2luZyBhbmQgcmFuZG9tIGZvcmVzdCBpbXByb3ZlIGFzIG1vcmUgdHJlZXMgYXJlIGFkZGVkLiBCb3RoIG1vZGVscyBzdGFydCB3aXRoIGhpZ2ggZXJyb3IgcmF0ZXMgYXJvdW5kIDQ1JSB3aGVuIHVzaW5nIGZldyB0cmVlcywgdGhlbiBxdWlja2x5IGltcHJvdmUgYXMgdHJlZXMgYXJlIGFkZGVkLiBCYWdnaW5nIGNvbnZlcmdlcyB0byBhYm91dCAzMS42JSBlcnJvciBhbmQgcmFuZG9tIGZvcmVzdCBjb252ZXJnZXMgdG8gYWJvdXQgMzEuMSUgZXJyb3IuIFJhbmRvbSBmb3Jlc3QgYWNoaWV2ZXMgbG93ZXIgZXJyb3Igd2l0aCBmZXdlciB0cmVlcyBpbml0aWFsbHksIHN1Z2dlc3RpbmcgaXQgbGVhcm5zIGZhc3RlciBhbmQgbW9yZSBlZmZpY2llbnRseS4gQnkgYXJvdW5kIDEwMCB0cmVlcywgYm90aCBtb2RlbHMgaGF2ZSBzdGFiaWxpemVkLCBtZWFuaW5nIGFkZGluZyBtb3JlIHRyZWVzIGJleW9uZCB0aGlzIHBvaW50IHByb3ZpZGVzIGxpdHRsZSBpbXByb3ZlbWVudC4gVGhlIHJlZCBsaW5lIGZvciBiYWdnaW5nIGlzIHNsaWdodGx5IGhpZ2hlciB0aGFuIHRoZSBibHVlIGxpbmUgZm9yIHJhbmRvbSBmb3Jlc3QgdGhyb3VnaG91dCwgc2hvd2luZyB0aGF0IHJhbmRvbSBmb3Jlc3QncyBzdHJhdGVneSBvZiBjb25zaWRlcmluZyBvbmx5IDMgcmFuZG9tIHZhcmlhYmxlcyBwZXIgc3BsaXQgbGVhZHMgdG8gYmV0dGVyIGZpbmFsIHBlcmZvcm1hbmNlLiBUaGlzIGRlbW9uc3RyYXRlcyB0aGF0IHJhbmRvbSBmb3Jlc3QncyBkaXZlcnNpdHktcHJvbW90aW5nIGFwcHJvYWNoIHdvcmtzIGJldHRlciB0aGFuIGJhZ2dpbmcncyBhcHByb2FjaCBvZiB1c2luZyBhbGwgdmFyaWFibGVzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIERpc2N1c3Npb24KCiMjIEtleSBGaW5kaW5ncwoKRW5zZW1ibGUgbWV0aG9kcyBsaWtlIGJhZ2dpbmcgYW5kIHJhbmRvbSBmb3Jlc3QgY29uc2lzdGVudGx5IG91dHBlcmZvcm0gc2luZ2xlIGRlY2lzaW9uIHRyZWVzLiBUaGUgZGV2aWFuY2UgdHJlZSBoYXMgYSB0b3RhbCBlcnJvciByYXRlIG9mIDMzLjYlIGFuZCBBVUMgb2YgMC42NSwgd2hpbGUgcmFuZG9tIGZvcmVzdCBhY2hpZXZlcyAzMS44JSBlcnJvciBhbmQgQVVDIG9mIDAuNzUuIEJhZ2dpbmcgYW5kIHJhbmRvbSBmb3Jlc3QgcmVkdWNlIG92ZXJmaXR0aW5nIGJ5IGNvbWJpbmluZyBwcmVkaWN0aW9ucyBmcm9tIG1hbnkgdHJlZXMgYnVpbHQgb24gZGlmZmVyZW50IGRhdGEgc2FtcGxlcy4gUmFuZG9tIGZvcmVzdCBwZXJmb3JtcyBldmVuIGJldHRlciB0aGFuIGJhZ2dpbmcgYmVjYXVzZSBpdCBhZGRzIHJhbmRvbW5lc3MgYnkgY29uc2lkZXJpbmcgb25seSAzIHZhcmlhYmxlcyBwZXIgc3BsaXQsIHdoaWNoIHJlZHVjZXMgY29ycmVsYXRpb24gYmV0d2VlbiB0cmVlcyBhbmQgaW1wcm92ZXMgZGl2ZXJzaXR5LiBBZ2UgYW5kIGhlaWdodCBhcmUgdGhlIG1vc3QgaW1wb3J0YW50IHByZWRpY3RvcnMgYWNyb3NzIGFsbCBtb2RlbHMuIEJvdGggZGVtb2dyYXBoaWMgZmFjdG9ycyByYW5rIGhpZ2hlc3QgaW4gdmFyaWFibGUgaW1wb3J0YW5jZSBtZWFzdXJlcywgaW5kaWNhdGluZyB0aGV5IGFyZSBmdW5kYW1lbnRhbCB0byBvYmVzaXR5IGNsYXNzaWZpY2F0aW9uLiBXYXRlciBpbnRha2UgKENIMk8pIGFuZCBlYXRpbmcgZnJlcXVlbmN5IChDQUVDKSBhcmUgdGhlIG5leHQgbW9zdCBpbXBvcnRhbnQsIHNob3dpbmcgdGhhdCBiZWhhdmlvcmFsIGZhY3RvcnMgYWxzbyBwbGF5IGEgbWFqb3Igcm9sZS4gRmFtaWx5IGhpc3Rvcnkgb2Ygb3ZlcndlaWdodCBtYXR0ZXJzIGZvciBvYmVzZSBwcmVkaWN0aW9ucy4gR2VuZGVyIGFuZCBzbW9raW5nIGFyZSBsZXNzIGltcG9ydGFudC4gVGhpcyBjb25zaXN0ZW5jeSBhY3Jvc3MgZGlmZmVyZW50IG1vZGVscyBzdWdnZXN0cyB0aGF0IHRoZXNlIHZhcmlhYmxlcyBjYXB0dXJlIHJlYWwgcGF0dGVybnMgaW4gb2Jlc2l0eSBhbmQgc2hvdWxkIGJlIHRoZSBmb2N1cyBvZiBpbnRlcnZlbnRpb24gc3RyYXRlZ2llcy4gQWdlLXJlbGF0ZWQgcGh5c2lvbG9naWNhbCBjaGFuZ2VzIGFuZCBib2R5IHN0cnVjdHVyZSAoaGVpZ2h0KSBhcmUgdGhlIHByaW1hcnkgZGV0ZXJtaW5hbnRzIG9mIG9iZXNpdHkgcmlzay4gQWxsIG1vZGVscyBzdHJ1Z2dsZSBtb3JlIHdpdGggb3ZlcndlaWdodCBhbmQgb2Jlc2UgY2xhc3NpZmljYXRpb24gY29tcGFyZWQgdG8gbm9ybWFsIHdlaWdodCBjbGFzc2lmaWNhdGlvbi4gVGhlIGRldmlhbmNlIHRyZWUgaGFzIG9ubHkgMy4xJSBlcnJvciBmb3Igbm9ybWFsIHdlaWdodCBidXQgNzYuOSUgZXJyb3IgZm9yIG92ZXJ3ZWlnaHQgYW5kIDgyLjQlIGZvciBvYmVzZS4gVGhpcyBpbWJhbGFuY2Ugb2NjdXJzIGJlY2F1c2Ugb3ZlcndlaWdodCBhbmQgb2Jlc2UgcGVvcGxlIHNoYXJlIHNpbWlsYXIgY2hhcmFjdGVyaXN0aWNzLCBtYWtpbmcgdGhlbSBoYXJkIHRvIGRpc3Rpbmd1aXNoLiBUaGUgb3ZlcndlaWdodCBjbGFzcyBhbHNvIGhhcyBmZXdlciBzYW1wbGVzICgxMzIgdnMgMzI3IG5vcm1hbCB3ZWlnaHQpLCBjcmVhdGluZyBhIGNsYXNzIGltYmFsYW5jZSBwcm9ibGVtLiBSYW5kb20gZm9yZXN0IHJlZHVjZXMgb2Jlc2UgZXJyb3IgdG8gNjQuNyUgYnV0IG92ZXJ3ZWlnaHQgcmVtYWlucyBkaWZmaWN1bHQgYXQgODAuOCUuIFRoZSBHaW5pIHRyZWUgcGVyZm9ybXMgYmVzdCBvbiBvdmVyd2VpZ2h0ICg2OS4yJSBlcnJvcikgYmVjYXVzZSBpdHMgbGFyZ2VyIHNpemUgYWxsb3dzIG1vcmUgY29tcGxleCBkZWNpc2lvbiBib3VuZGFyaWVzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBWYXJpYWJsZSBJbXBvcnRhbmNlIEludGVycHJldGF0aW9uCgpBZ2UgYW5kIGhlaWdodCBhcmUgdGhlIHN0cm9uZ2VzdCBwcmVkaWN0b3JzIG9mIG9iZXNpdHksIGNvbnNpc3RlbnRseSByYW5raW5nIGZpcnN0IGFuZCBzZWNvbmQgYWNyb3NzIGFsbCBtb2RlbHMuIEFnZSBtYXR0ZXJzIGJlY2F1c2Ugb2Jlc2l0eSByaXNrIGNoYW5nZXMgd2l0aCBsaWZlIHN0YWdl4oCUeW91bmdlciBwZW9wbGUgdGVuZCB0byBoYXZlIGRpZmZlcmVudCBtZXRhYm9saWMgcmF0ZXMgYW5kIGxpZmVzdHlsZXMgdGhhbiBvbGRlciBhZHVsdHMuIEhlaWdodCBpcyBpbXBvcnRhbnQgYmVjYXVzZSBCTUkgYW5kIG9iZXNpdHkgY2xhc3NpZmljYXRpb25zIGRlcGVuZCBvbiBib2R5IG1lYXN1cmVtZW50cyByZWxhdGl2ZSB0byBoZWlnaHQuIFRvZ2V0aGVyLCB0aGVzZSBkZW1vZ3JhcGhpYyBmYWN0b3JzIGV4cGxhaW4gbW9zdCBvZiB0aGUgdmFyaWF0aW9uIGluIG9iZXNpdHkgY2xhc3NpZmljYXRpb24uIFdhdGVyIGludGFrZSAoQ0gyTykgaXMgdGhlIHRoaXJkIG1vc3QgaW1wb3J0YW50IGJlaGF2aW9yYWwgZmFjdG9yLiBQZW9wbGUgd2hvIGRyaW5rIG1vcmUgd2F0ZXIgaGF2ZSBsb3dlciBvYmVzaXR5IHJpc2ssIGxpa2VseSBiZWNhdXNlIHdhdGVyIHJlcGxhY2VzIGhpZ2gtY2Fsb3JpZSBiZXZlcmFnZXMgYW5kIGhlbHBzIHdpdGggc2F0aWV0eS4gRWF0aW5nIGZyZXF1ZW5jeSBvdXRzaWRlIHRoZSBob21lIChDQUVDKSBpcyBhbHNvIGltcG9ydGFudCwgc3VnZ2VzdGluZyB0aGF0IGVhdGluZyBwYXR0ZXJucyBhbmQgd2hlcmUgcGVvcGxlIGVhdCBhZmZlY3RzIG9iZXNpdHkuIFBoeXNpY2FsIGFjdGl2aXR5IChGQUYpIGlzIGltcG9ydGFudCBmb3Igb2Jlc2UgY2xhc3NpZmljYXRpb24sIHNob3dpbmcgdGhhdCBleGVyY2lzZSBpcyBhIGtleSBmYWN0b3IgaW4gZGlzdGluZ3Vpc2hpbmcgb2Jlc2UgcGVvcGxlIGZyb20gb3RoZXIgZ3JvdXBzLiBHZW5kZXIsIHNtb2tpbmcsIGFuZCB0cmFuc3BvcnRhdGlvbiBtZXRob2QgaGF2ZSBsb3dlciBpbXBvcnRhbmNlIHNjb3Jlcy4gR2VuZGVyIHNob3dzIHNvbWUgZWZmZWN0IGJ1dCBpcyBsZXNzIHByZWRpY3RpdmUgdGhhbiBhZ2UgYW5kIGhlaWdodC4gU21va2luZyBoYXMgbWluaW1hbCBpbXBvcnRhbmNlLCBzdWdnZXN0aW5nIGl0IGlzIG5vdCBhIHN0cm9uZyBvYmVzaXR5IGluZGljYXRvciBpbiB0aGlzIGRhdGFzZXQuIFRyYW5zcG9ydGF0aW9uIG1ldGhvZCAoTVRSQU5TXzMpIHJhbmtzIGxvd2VzdCwgbWVhbmluZyB3aGV0aGVyIHBlb3BsZSB3YWxrLCB1c2UgY2Fycywgb3IgdXNlIHB1YmxpYyB0cmFuc3BvcnQgaGFzIGxpdHRsZSBlZmZlY3Qgb24gb2Jlc2l0eSBwcmVkaWN0aW9uIGFmdGVyIGFjY291bnRpbmcgZm9yIG90aGVyIGZhY3RvcnMuIFRoZSBudW1iZXIgb2YgbWFpbiBtZWFscyAoTkNQXzMpIGFuZCBjYWxvcmljIGJldmVyYWdlIGNvbnN1bXB0aW9uIChDQUxDXzMpIHNob3cgbW9kZXJhdGUgaW1wb3J0YW5jZSBidXQgYXJlIHdlYWtlciB0aGFuIHRoZSB0b3AgcHJlZGljdG9ycy4KCiMgQ29uY2x1c2lvbnMKCiMjIFN1bW1hcnkKClRoaXMgYW5hbHlzaXMgY29tcGFyZWQgZm91ciB0cmVlLWJhc2VkIG1vZGVscyBmb3Igb2Jlc2l0eSBjbGFzc2lmaWNhdGlvbjogc2luZ2xlIGRlY2lzaW9uIHRyZWVzIHVzaW5nIGRldmlhbmNlIGFuZCBHaW5pIHNwbGl0cywgYmFnZ2luZywgYW5kIHJhbmRvbSBmb3Jlc3QuIFJhbmRvbSBmb3Jlc3QgZW1lcmdlZCBhcyB0aGUgYmVzdCBtb2RlbCB3aXRoIDMxLjglIHRvdGFsIGVycm9yIGFuZCBBVUMgb2YgMC43NS4gQWdlIGFuZCBoZWlnaHQgYXJlIHRoZSBzdHJvbmdlc3QgcHJlZGljdG9ycyBvZiBvYmVzaXR5LCBmb2xsb3dlZCBieSBiZWhhdmlvcmFsIGZhY3RvcnMgbGlrZSB3YXRlciBpbnRha2UgYW5kIGVhdGluZyBmcmVxdWVuY3kuIEVuc2VtYmxlIG1ldGhvZHMgc2lnbmlmaWNhbnRseSBvdXRwZXJmb3JtIHNpbmdsZSB0cmVlcyBieSByZWR1Y2luZyBvdmVyZml0dGluZyBhbmQgY29tYmluaW5nIGRpdmVyc2UgcHJlZGljdGlvbnMuIFRoZSBtYWluIGNoYWxsZW5nZSBpcyBjbGFzc2lmeWluZyBvdmVyd2VpZ2h0IGNhc2VzIGR1ZSB0byBvdmVybGFwIHdpdGggbm9ybWFsIGFuZCBvYmVzZSBncm91cHMgYW5kIHNtYWxsZXIgc2FtcGxlIHNpemUuIFJhbmRvbSBmb3Jlc3QgYmFsYW5jZXMgYWNjdXJhY3kgYW5kIGVmZmljaWVuY3kgYnkgdXNpbmcgb25seSAzIHJhbmRvbSB2YXJpYWJsZXMgcGVyIHNwbGl0LCBhY2hpZXZpbmcgZmFzdGVyIGNvbnZlcmdlbmNlIHRoYW4gYmFnZ2luZyB3aGlsZSBtYWludGFpbmluZyBpbnRlcnByZXRhYmlsaXR5IG9mIHZhcmlhYmxlIGltcG9ydGFuY2UuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgRGlzY2xhaW1lcgoKMS0gVGhpcyBhbmFseXNpcyBpcyBmb3IgZWR1Y2F0aW9uYWwgYW5kIHJlc2VhcmNoIHB1cnBvc2VzIG9ubHkuIFRoZSBtb2RlbHMgZGV2ZWxvcGVkIGhlcmUgc2hvdWxkIG5vdCBiZSB1c2VkIGZvciBjbGluaWNhbCBkaWFnbm9zaXMgb3IgbWVkaWNhbCBkZWNpc2lvbi1tYWtpbmcgd2l0aG91dCB2YWxpZGF0aW9uIGJ5IHF1YWxpZmllZCBoZWFsdGhjYXJlIHByb2Zlc3Npb25hbHMuIE9iZXNpdHkgaXMgYSBjb21wbGV4IGNvbmRpdGlvbiBpbmZsdWVuY2VkIGJ5IGdlbmV0aWMsIGVudmlyb25tZW50YWwsIGJlaGF2aW9yYWwsIGFuZCBtZWRpY2FsIGZhY3RvcnMgbm90IGZ1bGx5IGNhcHR1cmVkIGJ5IHRoZSBhdmFpbGFibGUgZGF0YS4gSW5kaXZpZHVhbCBwcmVkaWN0aW9ucyBmcm9tIHRoZXNlIG1vZGVscyBtYXkgbm90IGFjY3VyYXRlbHkgcmVmbGVjdCBhY3R1YWwgb2Jlc2l0eSByaXNrLiBUaGUgZGF0YXNldCBpcyBwcmltYXJpbHkgc3ludGhldGljIGFuZCBtYXkgbm90IHJlcHJlc2VudCByZWFsIHBvcHVsYXRpb25zLiBSZXN1bHRzIHNob3VsZCBub3QgYmUgZ2VuZXJhbGl6ZWQgYmV5b25kIHRoZSBzcGVjaWZpYyBwb3B1bGF0aW9uIGFuZCBmZWF0dXJlcyBzdHVkaWVkLiBIZWFsdGhjYXJlIHByb3ZpZGVycyBzaG91bGQgdXNlIHRoZWlyIGNsaW5pY2FsIGp1ZGdtZW50IGFuZCBldmlkZW5jZS1iYXNlZCBndWlkZWxpbmVzIGZvciBvYmVzaXR5IGFzc2Vzc21lbnQgYW5kIHRyZWF0bWVudCwgbm90IHJlbHkgc29sZWx5IG9uIG1hY2hpbmUgbGVhcm5pbmcgcHJlZGljdGlvbnMuIFRoaXMgYW5hbHlzaXMgYXNzdW1lcyBkYXRhIHF1YWxpdHkgYW5kIGNvbXBsZXRlbmVzcyB3aXRob3V0IGV4dGVuc2l2ZSBkYXRhIHZhbGlkYXRpb24uIFVzZXJzIG9mIHRoZXNlIG1vZGVscyBhc3N1bWUgYWxsIHJlc3BvbnNpYmlsaXR5IGZvciBhcHByb3ByaWF0ZSBhcHBsaWNhdGlvbiBhbmQgaW50ZXJwcmV0YXRpb24gb2YgcmVzdWx0cy4gMi0gVGhpcyBhbmFseXNpcyB1c2VzIHRocmVlIGVsZW1lbnRzIG5vdCBjb3ZlcmVkIGluIE1RVDcwMTUgbGFiczogKipwUk9DIFBhY2thZ2UqKjogVGhlIGNvdXJzZSB1c2VzIHRoZSBgUk9DUmAgcGFja2FnZSBmb3IgUk9DIGFuYWx5c2lzLCB3aGljaCBvbmx5IHN1cHBvcnRzIGJpbmFyeSBjbGFzc2lmaWNhdGlvbi4gU2luY2Ugb3VyIHRhcmdldCB2YXJpYWJsZSBoYXMgMyBjbGFzc2VzIChOb3JtYWxfb3JfVW5kZXIsIE92ZXJ3ZWlnaHQsIE9iZXNlKSwgd2UgdXNlZCB0aGUgYHBST0NgIHBhY2thZ2UncyBgbXVsdGljbGFzcy5yb2MoKWAgZnVuY3Rpb24gdG8gY29tcHV0ZSBBVUMgZm9yIG11bHRpLWNsYXNzIHByb2JsZW1zLiAqKmdncGxvdDIgUGFja2FnZSoqOiBMaXN0ZWQgZm9yIHBvdGVudGlhbCB2aXN1YWxpemF0aW9uIGJ1dCBtaW5pbWFsbHkgdXNlZC4KCiMgUmVmZXJlbmNlcwoKWzFdIE1lbmRvemEgUGFsZWNob3IsIEYuLCAmIGRlIGxhIEhveiBNYW5vdGFzLCBBLiAoMjAxOSkuIERhdGFzZXQgZm9yIGVzdGltYXRpb24gb2Ygb2Jlc2l0eSBsZXZlbHMgYmFzZWQgb24gZWF0aW5nIGhhYml0cyBhbmQgcGh5c2ljYWwgY29uZGl0aW9uIGluIGluZGl2aWR1YWxzIGZyb20gQ29sb21iaWEsIFBlcnUgYW5kIE1leGljby4gRGF0YSBpbiBCcmllZiwgMjUsIDEwNDM0NC4gPGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouZGliLjIwMTkuMTA0MzQ0PiBbMl0gQ3JlbW9uYSwgTS4gQS4sICYgU2V2ZXJpbm8sIEYuICgyMDI0KS4gTVFUNzAxNS1MYWI2OiBBcmJyZXMgZGUgZMOpY2lzaW9uIFtEZWNpc2lvbiBUcmVlcyBMYWJdLiBDb3Vyc2UgbWF0ZXJpYWxzIGZvciBNUVQ3MDE1LiBbM10gQ3JlbW9uYSwgTS4gQS4sICYgU2V2ZXJpbm8sIEYuICgyMDI0KS4gTVFUNzAxNS1MYWI3OiBGb3LDqnRzIGFsw6lhdG9pcmVzIGV0IGFncsOpZ2F0aW9uIGRlcyBtb2TDqGxlcyBbUmFuZG9tIEZvcmVzdHMgYW5kIE1vZGVsIEFnZ3JlZ2F0aW9uIExhYl0uIENvdXJzZSBtYXRlcmlhbHMgZm9yIE1RVDcwMTUuCg==